【问题标题】:sqlite tuple comparisonsqlite 元组比较
【发布时间】:2011-04-18 05:20:09
【问题描述】:

尝试做与this question 相同的事情,但这次是在sqlite 中。在我当前的应用程序中,我需要能够执行这种类型的查询:

SELECT First, Last, Score
FROM mytable
WHERE
    ('John',  'Jordan',  5) <= (First, Last, Score )
    AND (First, Last, Score) <= ('Mike',  'Taylor',  50) 
ORDER BY First, Last, Score
LIMIT 1

得到('Liz', 'Jordan', 2)的答案,给定这个数据:

+-------+---------+-------+
| First | Last    | Score |
+-------+---------+-------+
| Liz   | Jordan  |     2 |
| John  | Jordan  |     2 |
| Liz   | Lemon   |    10 |
| Mike  | Taylor  |   100 |
| John  | Jackson |  1000 |
| Mike  | Wayne   |     1 |
| Liz   | Lemon   |    20 |
| Liz   | Meyers  |     5 |
| Bruce | Jackson |     1 |
+-------+---------+-------+

在 sqlite 中完成此任务的最有效方法是什么?请记住,这是一个玩具示例,我的实际应用程序的表具有更多的列和数据类型,以及数亿行。

如果解决方案可以轻松扩展到更多/更少的列,那就更好了。


元组比较:

元组按字典顺序排列,这意味着序列的顺序与它们的第一个不同元素相同。例如,(1,2,x)

值得注意的是,SQL-92(以及 mysql、oracle、postresql)正确地实现了这一点。该标准使用“行值构造函数”来表示我所说的元组。该行为在part 8.2.7, page 209 中进行了极其详细的定义。


这是创建示例所需的 SQL:

create table mytable ( First char(20), Last char(20), Score int );
insert into mytable values ('Liz', 'Jordan', 2);
insert into mytable values ('John', 'Jordan', 2);
insert into mytable values ('Liz', 'Lemon', 10);
insert into mytable values ('Mike', 'Taylor', 100);
insert into mytable values ('John', 'Jackson', 1000);
insert into mytable values ('Mike', 'Wayne', 1);
insert into mytable values ('Liz', 'Lemon', 20);
insert into mytable values ('Liz', 'Meyers', 5);
insert into mytable values ('Bruce', 'Jackson', 1);
create unique index 'UNIQ' on mytable (First, Last, Score);

【问题讨论】:

    标签: sql sqlite row-value-expression


    【解决方案1】:

    SQLite 不支持元组比较。但是行构造函数是一种速记。您可以使用更复杂的 WHERE 子句获得相同的结果。我省略了LIMIT 1 子句,以便更容易看到两个查询返回相同的集合。 (也就是说,在支持行构造函数的平台上。)

    这个比较

    ROW(a,b) <= ROW(c,d) 
    

    等价于

    a < c OR (a = c AND b <= d)
    

    您可以根据需要将其扩展到任意数量的列。

    SELECT First, Last, Score
    FROM mytable
    WHERE
          (('John' < First) OR 
           ('John' = First AND 'Jordan' < Last) OR 
           ('John' = First AND 'Jordan' = Last AND 5 <= Score))
      AND ((First < 'Mike') OR 
           (First = 'Mike' AND Last < 'Taylor') OR 
           (First = 'Mike' AND Last = 'Taylor' AND Score <= 50))
    ORDER BY First, Last, Score
    
    Liz  Jordan  2
    Liz  Lemon  10
    Liz  Lemon  20
    Liz  Meyers  5
    

    我没有在数据中使用 NULL 对此进行测试。


    截至 2018 年,SQLite确实支持元组比较。 OP 的查询使用提供的 SQL 语句产生预期的输出。这种编写查询的方式也有效。 (我发现between ... and ... 更具可读性。)

    SELECT First, Last, Score
    FROM mytable
    WHERE (First, Last, Score ) between ('John',  'Jordan',  5) and ('Mike',  'Taylor',  50) 
    ORDER BY First, Last, Score
    Limit 1
    

    我不知道这是多久以前介绍的。

    【讨论】:

    • 我想知道mysql优化器是否足够聪明,知道这与元组语法相同。
    • 过时了?根据这个网站,SQLite 支持元组比较:sql-workbench.net/dbms_comparison.html
    • @Andy:确实。你知道这是什么时候引入的吗?
    • @MikeSherrill'CatRecall' 不,不知道
    • row values 支持于 2016 年 10 月 14 日为 3.15.0 版添加,稍后有几个修复,最后一个在 2018-04-02 (3.23.0)。详情请见sqlite.org/changes.html
    【解决方案2】:

    我一直在通过使用字符串连接 (||) 和字符序列来避免缺少元组比较,以确保字段不会“合并”并导致不正确的匹配 (-)。

    (First, Last, Score) <= ('Mike',  'Taylor',  50)
    

    变成

    First||' - '||Last||' - '||Score <= 'Mike'||' - '||'Taylor'||' - '||'50'
    

    First||' - '||Last||' - '||Score <= 'Mike - Taylor - 50'
    

    所以你的SELECT 会是

    SELECT First, Last, Score
    FROM mytable
    WHERE
        'John - Jordan - 5' <= First||' - '||Last||' - '||Score
        AND First||' - '||Last||' - '||Score <= 'Mike - Taylor - 50'
    ORDER BY First, Last, Score
    LIMIT 1
    

    字符串连接非常昂贵且不那么简洁,但它的工作方式相同并且看起来非常相似。

    【讨论】:

    • 仅当您转义连接的字符串时,这才是通用的。您认为这比@catcall 的答案有更好的性能吗?
    猜你喜欢
    • 2011-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-25
    • 1970-01-01
    • 1970-01-01
    • 2022-12-17
    • 1970-01-01
    相关资源
    最近更新 更多