`
datamachine
  • 浏览: 155816 次
社区版块
存档分类
最新评论

集算器序表和SQL数据表的异同

    博客分类:
  • DB
阅读更多

  集算器序表和SQL数据表都是有结构的二维数据对象,都有记录、索引、主键的概念,都可以应用于结构化数据的计算。虽然都可以应用于结构化数据的计算,但两者的应用场景却有明显的区别,序表适合解决较复杂但数据量不是很大的计算问题,而数据表适合进行常规但可能数据量巨大的计算。

  两者的不同是由底层机制决定的。

  序表具有有序的特点,每条记录、每列数据都有确定的序号;序表支持显式集合,序表之间可以直接进行集合运算;序表也是泛型集合,其基本元素既可以是数值也可以是引用或者另一个集合。

  SQL数据表缺乏上述特点,但它对内外存透明,可以用一致的语法来访问内存、外存或混合数据。

  下面,我们将深入讨论两者的共同点和区别。

相同的基本功能

  集算器序表和SQL数据表都是有结构的二维数据对象,即以记录为基础,多条记录形成行式的二维表,二维表配合列名形成完整的数据结构。因为结构上大体相似,所以两者的基本用法区别不大。

  例1:查询对象中的数据。找到Freight大于100,并且是2013年以前的订单。

  SQL:SELECT * FROM Orders WHERE Freight > 100 AND OrderDate < ’1998-01-01′

  序表:= Orders.select(Freight > 100 && OrderDate < date(“1998-01-01″))

  注:本例使用的数据对象名为订单(Orders),后面还会用到客户(Customers)。

  例2:排序。将订单按EmployeeID正序排序,再按Freight逆序排序。

  SQL:SELECT * FROM Orders ORDER BY EmployeeID ,Freight DESC

  序表:=Orders.sort(EmployeeID,Freight:-1)

  例3:分组汇总。按员工汇总,对运货费求和,对订单计数。

  SQL:SELECT EmployeeID, COUNT(OrderID), SUM(Freight) FROM Orders GROUP BY EmployeeID

  序表:= Orders.groups(EmployeeID;sum(Freight),count(OrderID))

  例4:连接。将订单和客户这两个数据对象连接成新的数据对象,使用左连接,连接字段为CustomerID。

  SQL:Select * from Orders left join Customers on Orders.CustomerID =Customers.CustomerID

  序表:=join@1(Orders:, CustomerID;  Customers:, CustomerID)

  除了上述几种基本用法,集算器序表和SQL数据表在唯一值、计数、求和、平均、最大、最小值等算法上也非常相似,这里不再一一举例。

有序性的区别

  序表的记录集合是有序的,因此可以轻松解决和顺序有关的计算。SQL数据表缺乏序号及序号相关的访问方法,这使它在序运算上不够方便。

  例1:针对sales数据对象,计算每个月的销售额比上个月增长了百分之几。

  SQL:

    select salesAmount, salesMonth,

         (case when

    prev_price !=0 then ((salesAmount)/prev_price)-1

    else 0

    end) compValue

    from (select salesMonth, salesAmount,

    lag(salesAmount,1,0) over(order by salesMonth) prev_price

    from sales) t

  序表:

    sales.derive(salesAmount / salesAmount [-1]-1: compValue)

  比较:

  每个月的销售额和序无关,在序表和数据表中的表示方法一样,都是salesAmount。和序有关的是:上个月的销售额,即相对于当前记录的上一条记录中的salesAmount。序表有序,可以用salesAmount[-1]来直接表示上个月的销售额。SQL数据表无序,在SQL2003标准后添加了窗口函数补充了一些序运算功能,但仍然比较繁琐,需要用lag(salesAmount,1,0) over(order by salesMonth)这种复杂的方法来计算出上个月的销售额。

  序表还可以轻松表达相对区间,比如相对于当前月份的前两个月及后两个月共五个月的销售额,序表可以这样表达:salesAmount{-2,2}。在SQL中使用窗口函数也可以表达区间的汇总运算,但要麻烦得多。

  例2:针对sales数据表,找出每种产品里销售额最高的前10条的记录。

  SQL:

    select salesMan, product ,amount

    from ( select salesMan, product ,amount, rank() over (partition by product order by amount desc ) ranking from sales)

    where ranking <=10

  序表:

    = sales.group(product).(~.top(-amount;10))

  比较:

  实现本例最直观的思路是将数据按产品分组,然后再进行分组内的序运算,简单的方法是直接取组内amount最小的前十条记录,直观的方法是按照amount对组内数据逆序排序,再取组内序号是1到10的记录。

  序表对序运算支持良好,可以用top函数实现第一种算法(如例子中),也可以用sort函数和记录序号实现第二种算法,即:=sales.group(product).(~.sort(Amount:-1)).(~([to(10)]))。

  SQL数据表记录没有次序,必须先计算出一个序号或者可代替序号功能的字段,比如排名。上述例子的算法就是先计算出组内数据的排名,再取排名前十的记录。显而易见,SQL算法有点曲折,语法也难懂,要用到复杂的窗口函数over (partition by…… order by……),以及难以跟踪调试的子查询。

  相比之下,序表更加直接简便,也容易跟踪调试。比如程序员可以先写出=sales.group(product)这句代码进行测试,这句代码表示将数据分组,它可以独立运行并独立显示结果。如果分组结果符合预期,程序员可以继续加入第二段代码:对组内数据逆序排序,即.(~.sort(Amount:-1)),这里的“.”表示把前面的计算结果作为一个整体继续加工,“~”表示当前的组内数据,“-1”表示逆序。现在的代码是=sales.group(product).(~.sort(Amount:-1)),它仍然可独立运行并独立显示结果。观察或调试后,程序员可以加入第三段代码:取组内序号是1到10的记录,即.(~([to(10)]))。

  可以看到,序表的计算过程可以步步递进,在解决复杂的计算问题时简化计算防止出错。事实上,上述连续的三段代码可以写成逐步引用的三行代码,这可以更加清晰地分解计算目标:

  值得注意的是,SQL无法对数据先分组再执行有序计算,即使用临时表也无法实现,它必须将这两步合二为一,产生这一现象的原因是数据表不支持显式集合和泛型集合,语法表达能力较弱。

  另外,SQL使用的窗口函数虽然是ansi标准,但数据库厂商并未完全按标准实现,不同的数据库里写法也会不同,有些数据库根本不提供窗口函数。而序表的函数语法独立于数据源,不论哪种数据源(包括数据库、Txt文件、Excel文件、二进制文件等),使用序表计算时都无需修改代码。

显式集合的区别

  SQL有集合的概念,但不提供显式的集合,不能作为独立的变量存在,只能借助临时表来实现集合运算。而序表是真正的显式集合,可以实现集合运算。

  例子:针对Contract数据对象进行计算,假设业务上将订购数量大于40的合同称为大合同,单价大于2000的合同称为重要合同,请找出既是大合同又是重要合同的当年合同,以及除此之外的其他合同。

  SQL

    select SellDate,Quantity,Amount,Client from Contract where to_char(SellDate,’yyyy’)=’2014′ and quantity>=40 and AMOUNT>=2000

    select SellDate,Quantity,Amount,Client from Contract where not(to_char(SellDate,’yyyy’)=’2014′ and quantity>=40 and AMOUNT>=2000)

  序表

    =thisYear= Contract.select(year(SellDate)=2014)

    =big= Contract.select(Quantity>40)

    =importance = Contract.select(AMOUNT>2000)

    =answer=thieYear^big^ importance

    =others= Contract\answer

  比较分析

  既是大合同又是重要的当年合同,这是比较典型的自然思考方式,使用交集运算最为直观。如果将大合同定义为big,重要合同定义为importance,当年的合同定义为thisYear,那我们可以很容易写出伪代码:big∩importance∩thiYear。序表是显式集合,可以很直观地表达等价的算法,即:thieYear^big^ importance。SQL不能用集合变量来表示,因此只能寻求其他方式,比如转换为布尔条件来表示,即例子中的: to_char(SellDate,’yyyy’)=’2014′ and quantity>=40 and AMOUNT>=2000。

  第一问比较简单,因此两者的开发难度并没有太大的区别。随着问题的深入,两者的区别才会明显起来。

  第二问:“除此之外的其他合同”,这也是典型自然思考方式,使用差集可以一步算出。序表的算法是:Contract\answer,非常直观。使用布尔条件,SQL也可以计算出答案,但表达式的写法和业务描述相差甚远,即:not(to_char(SellDate,’yyyy’)=’2014′ and quantity>=40 and AMOUNT>=2000)。

  SQL也可以用集合运算来解答,算法也很直观,但是因为数据表不能用集合变量来表示,代码会显得非常冗长:

    (select SellDate,Quantity,Amount,Client from Contract)

    minus

    (Select select SellDate,Quantity,Amount,Client from Contract from(

    (select SellDate,Quantity,Amount,Client from Contract where to_char(SellDate,’yyyy’)=’2012′)

    Intersect

    (select SellDate,Quantity,Amount,Client from Contract where quantity>=40)

Intersect

(select SellDate,Quantity,Amount,Client from Contract where AMOUNT>=2000))

  因为代码太冗长,所以很多人宁可使用布尔条件来间接实现集合运算。

  当然,很多情况下数据表使用集合运算会比布尔条件更加方便,比如多个物理表之间的集合运算,或多层子查询之间的集合运算。这种情况下,将集合运算转化为布尔条件的代价就太高了,程序员必须接受冗长的集合运算。

泛型集合的区别

  序表是泛型集合,除了存储实体数据,还可以存储指向关联数据的引用,这就使序表可以通过直观的对象引用实现关联计算。数据表只能存储实体数据,需要用复杂的关联语句才能完成等价的计算。

  例子

  请计算获得总裁奖的部门经理们,其下属的年度优秀员工们都有谁。这里涉及两个数据对象:department和employee,其中,department的deptName字段和employee的empDept是一对一的关系,department中的manager字段和employee中的empName也是一对一的关系。另外,总裁奖的代码是PA;年度优秀员工的代码是EOY。

  SQL

    SELECT A.*

    FROM employee A,department B,employee C

    WHERE A.empDept=B.deptName AND B.manager=C.empName AND A.empHonor=‘EOY’ AND C.empHornor=‘PA’

  序表:

    employee.select(empHonor: “EOY”,empDept.manager.empHornor:”PA”)

  比较分析

  SQL的解法无疑是正确的,但其中的关联语句比较复杂,普通程序员理解费力。序表的解法比较直观,empHonor:”EOY”是条件之一:“年度优秀员工们都有谁”,而empDept.manager.empHornor表示员工的“所属部门.该部门的经理.该经理的获奖情况”,显然,这个值为PA就符合题目中的条件二:“获得总裁奖的部门经理们”。这就是对象引用。

  对象引用允许程序员使用“.”操作符来引用相关数据,这可以将业务中的关联关系直观地翻译为计算机语言,可以方便地表达多层关系,可以直观地进行关联计算。

对内外存透明的区别

  SQL

  SQL数据表因为不支持泛型和集合数据,内存中的数据写到外存时不会丢失信息,因而具有对内外存运算的透明性。首次访问数据表时,数据通常来自外存;之后再访问同样的数据表时,数据就可以来自内存缓存;对于数据量较大的数据表,其一部分数据来自外存,而另一部分会来自于内存。无论数据是来自于内存还是外存,无论数据量是大还是小,数据表的访问语法并无区别,程序员无需为此书写不同的SQL语句。

  序表

  序表支持了泛型(特别是引用)和集合数据,内存中的数据写到外存时会丢失信息,不能再读入,这导致其运算无法对内外存透明。序表是纯内存数据对象,所能计算的数据量受到内存限制,不能太大;如果数据量较大,那就应当使用游标(集算器的另一种数据对象)来进行外存计算,而游标和序表的语法是不同的;如果想提高性能或进行业务逻辑复杂的计算,程序员还必须将数据在游标和序表之间进行转换。

  比较

  序表对内外存不透明,程序员需要书写不同的代码来适应内存、外存或混合计算,还需要为将来数据量的增长而修改代码,因此前期设计和后期维护的工作量较大。而SQL数据表对内外存透明,程序员只需书写一套代码就能适应不同规模的数据,设计和维护的工作量较小。

  通过上述比较分析我们可以看出:数据有序、显示集合、泛型集合,序表的这些特点使它可以轻松解决和顺序有关的复杂问题,可以简化集合运算的复杂度,可以用直观的对象引用来处理复杂的多表关联。而SQL数据表对内外存透明,代码通用性更好。 

  • 大小: 10.1 KB
0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics