欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-31 21:17:13
...

  集算器序表和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数据表的异同
            
    
    博客分类: DB sql数据表序表集算器

  值得注意的是,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数据表对内外存透明,代码通用性更好。 

  • 集算器序表和SQL数据表的异同
            
    
    博客分类: DB sql数据表序表集算器
  • 大小: 10.1 KB