美文网首页我爱编程DDIA
数据模型和查询语言 -- 数据查询语言

数据模型和查询语言 -- 数据查询语言

作者: 瑞_xlows | 来源:发表于2018-04-07 18:05 被阅读0次

    当引入关系模型时,它包含了一种查询数据的新方法:SQL是一种声明式查询语言,而IMS和CODASYL则使用命令式代码查询数据库。这是什么意思?

    许多常用的编程语言是必需的。举个例子,如果你有一份动物物种的清单,你可能会写这样的东西来只返回列表中的鲨鱼:

    function getSharks()
    {    
        var sharks = [];    
        for (var i = 0; i < animals.length; i++) {
            if (animals[i].family === "Sharks") { 
               sharks.push(animals[i]);       
            }   
        }    
        return sharks; 
    }
    

    在关系代数中,你可以这样写:

    sharks  =  σfamily = “Sharks” (animals)
    

    σ(希腊字母sigma)是选择操作符,只返回与条件=“鲨鱼”相匹配的动物。

    当定义SQL时,它非常接近于关系代数的结构:

    SELECT * FROM animals WHERE family = 'Sharks'; 
    

    命令式语言告诉计算机以一定的顺序执行某些操作。您可以想象一下,通过一行代码来遍历代码行,评估条件,更新变量,并决定是否再次绕圈子。

    在声明式查询语言中,如SQL或关系代数,您只需要指定您想要的数据的模式——结果必须满足什么条件,以及您希望如何转换数据(例如,排序、分组和聚合)——而不是如何实现这个目标。这取决于数据库系统的查询优化器来决定要使用哪些索引和哪些联接方法,以及执行查询的各个部分的顺序。

    声明式查询语言具有吸引力,因为它通常比命令式API更简洁、更容易使用。但更重要的是,它还隐藏了数据库引擎的实现细节,这使得数据库系统能够在不需要对查询进行任何更改的情况下引入性能改进。

    例如,在本节开始的命令式代码中,动物列表以特定的顺序出现。如果数据库想要在幕后回收未使用的磁盘空间,它可能需要移动记录,改变动物出现的顺序。数据库可以安全地完成吗,不会破坏查询?

    SQL示例并不能保证任何特定的顺序,因此,如果订单发生变化,它也不介意。但是,如果查询被写成命令式代码,那么数据库永远不能确定代码是否依赖于顺序。SQL在功能上更加有限,这一事实为数据库提供了更多的自动优化空间。

    最后,声明性语言常常使自己能够并行执行。现在,通过添加更多的内核,cpu的速度越来越快,而不是以比以前更高的时钟速度运行。命令式代码很难在多个核心和多台机器上并行化,因为它指定了必须按照特定顺序执行的指令。声明性语言更有可能在并行执行中获得更快的速度,因为它们只指定结果的模式,而不是用来确定结果的算法。如果合适的话,数据库可以自由地使用查询语言的并行实现。

    Web上的声明性查询

    声明式查询语言的优点并不局限于数据库。为了说明这一点,让我们在一个完全不同的环境中比较声明式和命令式方法:web浏览器。

    假设你有一个关于海洋中动物的网站。用户目前正在浏览鲨鱼的页面,所以你可以在当前选择的“鲨鱼”上标记“鲨鱼”,如下:

    <ul>    
      <li class="selected">
            <p>Sharks</p>
             <ul>
                <li>Great White Shark</li>
                <li>Tiger Shark</li>
                <li>Hammerhead Shark</li>
            </ul>
      </li>    
      <li>
            <p>Whales</p>
            <ul>
                <li>Blue Whale</li>
                <li>Humpback Whale</li>
                <li>Fin Whale</li>
            </ul>
       </li> 
    </ul>
    

    现在,您希望当前选中的页面的标题具有蓝色背景,让它在视觉上突出显示。使用CSS很简单:

      li.selected > p 
      { background-color: blue; }
    

    这里,CSS选择器li.selected > p声明了我们想要应用蓝色样式元素的模式:即所有的<p>元素,它们的直接父元素是一个带有CSS类的<li>元素。在例子中,<p>鲨鱼</p>与这种模式相匹配,但是<p>鲸鱼</p>不匹配,因为它的<li>父母缺少class=的“selected”。

    如果您使用的是XSL而不是CSS,您可以做一些类似的事情:

    <xsl:template match="li[@class='selected']/p">    
      <fo:block background-color="blue">        
        <xsl:apply-templates/>    
      </fo:block> 
    </xsl:template>
    

    Here, the XPath expression li[@class='selected']/p is equivalent to the CSS selector li.selected > p in the previous example. What CSS and XSL have in common is that they are both declarative languages for specifying the styling of a document.

    在这里,XPath表达式 li[@class='selected']/p 相当于之前例子中的CSS选择器 li.selected > p 。CSS和XSL的共同之处在于,它们都是用于指定文档样式的声明性语言。

    想象一下,如果你必须使用命令式的方法,生活会是什么样子。在JavaScript中,使用core Document Object Model(DOM)API,结果可能是这样的:

    var liElements = document.getElementsByTagName("li"); 
    for (var i = 0; i < liElements.length; i++) {
      if (liElements[i].className === "selected") { 
        var children = liElements[i].childNodes;  
        for (var j = 0; j < children.length; j++) { 
          var child = children[j];  
          if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "P") {    
            child.setAttribute("style", "background-color: blue"); 
          }    
        }   
      }
    }
    

    这个JavaScript会将<p>Sharks</p>设置为蓝色背景,但是代码很糟糕。它不仅比CSS和XSL的同样的实现方式代码更长、更难以理解,而且还存在一些严重的问题:

    • 如果所选的class被删除(例如,由于用户单击不同的页面),即使代码重新运行,蓝色也不会被删除,因此该条目将保持高亮显示,直到整个页面被重新加载。使用CSS,浏览器会自动检测到li.selected > p 规则不再适用,并在选定的类被移除后立即删除蓝色背景。

    • 如果您想要利用一个新的API,比如document.getElementsBy ClassName("selected") ,甚至是 document.evaluate() ——这可能会提高性能——您必须重写代码。另一方面,浏览器厂商可以在不破坏兼容性的情况下提高CSS和XPath的性能。

    在web浏览器中,使用声明式的CSS样式比在JavaScript中强制操作样式要好得多。类似地,在数据库中,像SQL这样的声明式查询语言比命令式查询APIs要好得多。

    MapReduce查询

    MapReduce是一种用于在许多机器中批量处理大量数据的编程模型,由Google推广。MapReduce的有限形式由一些NoSQL数据存储支持,包括MongoDB和CouchDB,作为一种在许多文档中执行只读查询的机制。

    现在,我们将简要讨论MongoDB对该模型的使用。

    MapReduce既不是声明性的查询语言,也不是完全命令式的查询API,而是介于两者之间:查询的逻辑是用代码片段来表达的,这是由处理框架反复调用的。它基于map和reduce函数,这些函数存在于许多函数式编程语言中。

    举个例子,假设你是一名海洋生物学家,每次你在海洋中看到动物时,你都会在你的数据库中添加一个观察记录。现在你想要生成一份报告,说明你每个月看到了多少鲨鱼。

    在PostgreSQL中,您可以像这样表示这个查询:

    SELECT date_trunc('month', observation_timestamp) AS observation_month, sum(num_animals) AS total_animals
    FROM observations 
    WHERE family = 'Sharks' 
    GROUP BY observation_month;
    

    这个查询首先过滤观察结果,只显示鲨鱼家族中的物种,然后在它们发生的月里对观测结果进行分组,最后将观察到的动物数量加起来。

    MongoDB的MapReduce特性也可以用如下方式来表达:

    db.observations.mapReduce(    
      function map() {         
        var year  = this.observationTimestamp.getFullYear();
        var month = this.observationTimestamp.getMonth() + 1; 
        emit(year + "-" + month, this.numAnimals); 
      },    
      function reduce(key, values) {
        return Array.sum(values);     
      },    
      {        
        query: { family: "Sharks" },         
        out: "monthlySharkReport"    
      } 
    );
    

    例如,观察收集包含这两个文档:

    {    
      observationTimestamp: Date.parse("Mon, 25 Dec 1995 12:34:56 GMT"),    
      family:     "Sharks",    
      species:    "Carcharodon carcharias",    
      numAnimals: 3 
    } 
    {    
      observationTimestamp: Date.parse("Tue, 12 Dec 1995 16:17:18 GMT"),    
      family:     "Sharks",    
      species:    "Carcharias taurus",    
      numAnimals: 4 
    }
    
    

    每个文档都将调用map函数,从而导致发出(“1995-12”、3)和发出(“1995-12”,4),随后,reduce函数将被称为reduce(“1995-12”、3、4),返回7。

    map和reduce函数在它们被允许做的事情上受到了一定的限制。它们必须是纯函数,这意味着它们只使用传递给它们的数据作为输入,它们不能执行额外的数据库查询,而且它们不能有任何副作用。这些限制允许数据库在任何地方以任何顺序运行这些函数,并在故障时重新运行它们。然而,它们仍然很强大:它们可以解析字符串、调用库函数、执行计算等等。

    MapReduce是一种相当低级的编程模型,用于在一组机器上进行分布式执行。像SQL这样的高级查询语言可以作为MapReduce操作的管道实现,但是也有许多不使用MapReduce的SQL的分布式实现。请注意,SQL中没有任何东西限制它在一台机器上运行,而MapReduce并没有对分布式查询执行的垄断。

    能够在查询中使用JavaScript代码对于高级查询来说是一个很好的特性,但是它并不局限于MapReduce——一些SQL数据库也可以用JavaScript函数扩展。

    MapReduce的一个可用性问题是,您必须编写两个经过仔细协调的JavaScript函数,这通常比编写单个查询要困难得多。此外,声明式查询语言为查询优化器提供了更多的机会来提高查询的性能。由于这些原因,MongoDB 2.2增加了一种称为聚合管道声明性查询语言的支持。在这种语言中,相同的鲨鱼计数查询是这样的:

    db.observations.aggregate([
        { $match: { family: "Sharks" } },
        { $group: {
            _id: {
                year:  { $year:  "$observationTimestamp" },
                month: { $month: "$observationTimestamp" }
            },
            totalAnimals: { $sum: "$numAnimals" }
        } }
     ]);
    

    聚合管道语言在表达性方面与SQL的子集相似,但是它使用基于json的语法,而不是SQL风格的语法;这种差别可能是品味的问题。这个故事的寓意是,NoSQL系统可能会发现自己意外地重新发明了SQL,尽管是伪装的。

    相关文章

      网友评论

        本文标题:数据模型和查询语言 -- 数据查询语言

        本文链接:https://www.haomeiwen.com/subject/guvyhftx.html