为啥mysql和sqlite之间的SELECT结果不同?

Posted

技术标签:

【中文标题】为啥mysql和sqlite之间的SELECT结果不同?【英文标题】:Why does SELECT results differ between mysql and sqlite?为什么mysql和sqlite之间的SELECT结果不同? 【发布时间】:2012-04-16 09:11:46 【问题描述】:

我以简化和扩展的方式重新询问此question。

考虑这些 sql 语句:

create table foo (id INT, score INT);

insert into foo values (106, 4);
insert into foo values (107, 3);
insert into foo values (106, 5);
insert into foo values (107, 5);

select T1.id, avg(T1.score) avg1
from foo T1
group by T1.id
having not exists (
    select T2.id, avg(T2.score) avg2
    from foo T2
    group by T2.id
    having avg2 > avg1);

使用 sqlite,select 语句返回:

id          avg1      
----------  ----------
106         4.5       
107         4.0       

mysql 返回:

+------+--------+
| id   | avg1   |
+------+--------+
|  106 | 4.5000 |
+------+--------+

据我所知,mysql 的结果是正确的,而 sqlite 的结果是不正确的。我尝试使用 sqlite 转换为real,如下所示,但它仍然返回两条记录:

select T1.id, cast(avg(cast(T1.score as real)) as real) avg1
from foo T1
group by T1.id
having not exists (
    select T2.id, cast(avg(cast(T2.score as real)) as real) avg2
    from foo T2
    group by T2.id
    having avg2 > avg1);

为什么sqlite返回两条记录?

快速更新

我针对最新的 sqlite 版本 (3.7.11) 运行了语句,仍然得到两条记录。

另一个更新

我向 sqlite-users@sqlite.org 发送了一封关于该问题的电子邮件。

我自己,我一直在玩 VDBE,发现了一些有趣的东西。我拆分了not exists 的每个循环的执行跟踪(每个平均组一个)。

为了拥有三个平均组,我使用了以下语句:

create table foo (id VARCHAR(1), score INT);

insert into foo values ('c', 1.5);
insert into foo values ('b', 5.0);
insert into foo values ('a', 4.0);
insert into foo values ('a', 5.0);

PRAGMA vdbe_listing = 1;
PRAGMA vdbe_trace=ON;

select avg(score) avg1
from foo
group by id
having not exists (
    select avg(T2.score) avg2
    from foo T2
    group by T2.id
    having avg2 > avg1);

我们清楚地看到,不知何故,应该是r:4.5 变成了i:5

我现在正试图找出原因。

最终编辑

所以我已经玩够了 sqlite 源代码。我现在更了解这头野兽了,尽管我会让original developer 解决它,因为他似乎已经这样做了:

http://www.sqlite.org/src/info/430bb59d79

有趣的是,至少对我而言,似乎较新的版本(有时在我使用的版本之后)支持插入在上述提交中添加的测试用例中使用的多条记录:

CREATE TABLE t34(x,y);
INSERT INTO t34 VALUES(106,4), (107,3), (106,5), (107,5);  

【问题讨论】:

只是为了好玩,我将它运行到 SQL,SQL Server 会产生什么,它抱怨 avg2avg1 不存在。我用MAX(T2.score)MAX(T1.score) 替换了它们,它给出了SQLite 结果。当我使用score REAL 创建表时,它给出了 MySQL 结果。也许您的 MySQL 架构与 sqlites 不同? @ta.speot.is: 你能尝试像avg(T2.score) as avg2 一样添加as(出现两次)吗? 不起作用。可以肯定的是,在 WHEREGROUP BYHAVING 中使用别名时,SQL Server 不会掷骰子。 在 sqlite 下尝试部分语句我有这个SQL error: no such function: exists。在 mysql 下工作的关键字可能不在其他数据库系统中。我会亲自使用IN 关键字来测试您的子查询的结果 似乎 sqlite 团队需要一份错误报告。 【参考方案1】:

我试图弄乱一些查询变体。

似乎 sqlite 在嵌套 HAVING 表达式中使用先前声明的字段时出现错误。

在您的示例中,avg1 在第二次拥有下始终等于 5.0

看:

select T1.id, avg(T1.score) avg1
from foo T1
group by T1.id
having not exists (
    SELECT 1 AS col1 GROUP BY col1 HAVING avg1 = 5.0);

这个不返回任何内容,但执行以下查询会返回两条记录:

...
having not exists (
    SELECT 1 AS col1 GROUP BY col1 HAVING avg1 <> 5.0);

我在sqlite tickets list 找不到任何类似的错误。

【讨论】:

是的,我在使用 VDBE 进行跟踪时看到了一些非常相似的东西。我已经向 sqlite-users@sqlite.org 发送了一封关于该问题的电子邮件。【参考方案2】:

让我们看看这两种方式,我将使用 postgres 9.0 作为我的参考数据库

(1)

-- select rows from foo 

select T1.id, avg(T1.score) avg1
from foo T1
group by T1.id
-- where we don't have any rows from T2
having  not exists (
-- select rows from foo
select T2.id, avg(T2.score) avg2
from foo T2
group by T2.id
-- where the average score for any row is greater than the average for 
-- any row in T1
having avg2 > avg1);

 id  |        avg1        
-----+--------------------
 106 | 4.5000000000000000
(1 row)

然后让我们移动子查询中的一些逻辑,去掉'not': (2)

-- select rows from foo 
select T1.id, avg(T1.score) avg1
from foo T1
group by T1.id
-- where we do have rows from T2
having  exists (
-- select rows from foo
select T2.id, avg(T2.score) avg2
from foo T2
group by T2.id
-- where the average score is less than or equal than the average for any row in T1
having avg2 <= avg1);
-- I think this expression will be true for all rows as we are in effect doing a
--cartesian join 
-- with the 'having' only we don't display the cartesian row set

 id  |        avg1        
-----+--------------------
 106 | 4.5000000000000000
 107 | 4.0000000000000000
(2 rows)

所以你必须问自己——当你在有子句中执行这个相关子查询时,你的实际意思是什么,如果它根据主查询中的每一行评估每一行,我们正在做一个笛卡尔连接,而我没有'不认为我们应该指责 SQL 引擎。

如果您希望每一行都小于最大平均值您应该说的是:

select T1.id, avg(T1.score) avg1 
from foo T1 group by T1.id
having avg1 not in 
(select max(avg1) from (select id,avg(score) avg1 from foo group by id)) 

【讨论】:

我问题中的那个select语句真的不难理解;我只是想要正确的结果;-) 我认为它是模棱两可的,并且会在不同的技术中提供不确定的结果 似乎对MySql、MS Sql、现在的Postgres 没有歧义;而且似乎 sqlite 的主要开发人员正在对我发送给 sqlite-users@sqlite.org 的电子邮件做出反应。【参考方案3】:

你试过这个版本吗? :

select T1.id, avg(T1.score) avg1
from foo T1
group by T1.id
having not exists (
    select T2.id, avg(T2.score) avg2
    from foo T2
    group by T2.id
    having avg(T2.score) > avg(T1.score));

还有这个(应该给出相同的结果):

select T1.*
from
  ( select id, avg(score) avg1
    from foo 
    group by id
  ) T1
where not exists (
    select T2.id, avg(T2.score) avg2
    from foo T2
    group by T2.id
    having avg(T2.score) > avg1);

查询也可以用派生表来处理,而不是HAVING子句中的子查询:

select ta.id, ta.avg1
from 
  ( select id, avg(score) avg1
    from foo
    group by id
  ) ta
  JOIN
  ( select avg(score) avg1
    from foo 
    group by id
    order by avg1 DESC
    LIMIT 1
  ) tmp
  ON tmp.avg1 = ta.avg1 

【讨论】:

我刚刚做了; sqlite中相同的2条记录,mysql中的相同1条记录。 是的,您的第二个选项我已经作为答案;请参阅我在此问题开头链接的问题。 至于为什么原始查询不能按预期工作,我想这是一个错误,与子查询的处理方式有关。 @sixfeetsix:JOIN 版本是否显示正确的(1 行)结果? @sixfeetsix:我添加了一个带有派生子查询的版本,将HAVING 转换为WHERE

以上是关于为啥mysql和sqlite之间的SELECT结果不同?的主要内容,如果未能解决你的问题,请参考以下文章

SqlLite 和 MariaDb SELECT 之间的结果不同

MySQL中为啥不能在select语句中使用into将查询结果存入新表,但是SQL sever可以

在 SQLite 中获取日期范围之间的数据

两个 SELECT 语句之间的 MySQL 笛卡尔积

如何在mysql和sqlite之间同步数据?

为啥选择查询在 mysql 集群中显示未排序的结果?