【发布时间】:2015-07-07 09:58:46
【问题描述】:
我遇到了由错误查询引起的数据丢失问题。
数据已恢复,但现在我想了解一下问题所在。
我在 SQL Server 2014 上遇到了这个问题,但我在 SQL Server 2000 和 PostgreSQL 上复制了它。具体来说,有一个 DELETE。在以下场景中,我使用 SELECT。
为 sql server 2014 创建表:
CREATE TABLE [dbo].[tmp_color](
[color_id] [int] NOT NULL,
[color_name] [nvarchar](50) NOT NULL,
[color_cat] [int] NOT NULL,
CONSTRAINT [PK_tmp_color] PRIMARY KEY CLUSTERED (
[color_id] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF
, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[tmp_color_cat](
[catid] [int] NOT NULL,
[catname] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_tmp_color_cat] PRIMARY KEY CLUSTERED (
[catid] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF
, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
还有 Postgres 版本:
CREATE TABLE tmp_color (
color_id integer NOT NULL,
color_name text,
color_cat integer,
CONSTRAINT tmp_color_pkey PRIMARY KEY (color_id)
);
CREATE TABLE tmp_color_cat (
catid integer NOT NULL,
catname text,
CONSTRAINT tmp_color_cat_pkey PRIMARY KEY (catid)
);
数据填充(适用于两个 RDBMS):
INSERT INTO tmp_color_cat (catid, catname) VALUES (1, 'magic color');
INSERT INTO tmp_color_cat (catid, catname) VALUES (2, 'normal color');
INSERT INTO tmp_color (color_id, color_name, color_cat) VALUES (1, 'red', 1);
INSERT INTO tmp_color (color_id, color_name, color_cat) VALUES (2, 'green', 2);
INSERT INTO tmp_color (color_id, color_name, color_cat) VALUES (3, 'black', 1);
以下 SELECT 错误:
SELECT color_cat
FROM tmp_color_cat;
因为color_cat 不存在于tmp_color_cat。
但是,当您在子查询中使用它时:
SELECT * FROM tmp_color
WHERE color_cat IN(
SELECT color_cat
FROM tmp_color_cat
WHERE catname = 'magic color'
);
它返回来自tmp_color 的每条记录。
脚本中的逻辑错误很明显:开发人员写了错误的列来识别类别。如果您要删除记录而不是选择它们,您将删除整个表格。不好。
这是期望的行为吗?还是子查询设计的结果?
通过观察SQL Server的执行计划,逻辑操作是Left Semi Join。
我找到了几个帖子,一个 for PostgreSQL 和一个 for SQL Server。有没有什么好的文档可以发送给开发者组,解释为什么这不是错误?
如何避免此类问题?我的第一个想法是使用别名。别名很好。
【问题讨论】:
-
您的第二个链接似乎正是您所描述的,所以我不太明白您的问题。我建议始终在涉及多个表的情况下使用别名,即使看起来没有必要,这是一种很好的编程习惯(如您的事件所示)
-
子选择中的
color_cat引用了外部查询中的列,并且实质上将查询更改为where color_cat = colo_cat。这就是 SQL 标准定义可见性规则的方式(如您的第一个链接中所述)。最好的方法(正如您已经注意到的)是始终使用别名和完全限定的列名 (alias.column),然后这个错误就会变得很明显。 -
总是。采用。别名。 :) 即使是从单个表查询 - 稍后您可能会使用另一个表扩展此查询,然后您可能会再次遇到问题。
-
是的,@a_horse_with_no_name,问题很清楚。我的环境中的问题出在 SQL Server 中。第二个链接是发送给开发人员的好资源吗?
-
我们可以假设当前的 Postgres 版本是 9.4?
标签: sql sql-server postgresql