在数据库查询中,DISTINCT关键字经常被用来去除查询结果中的重复行。然而,它的使用并不总是正确的,有时它可能只是隐藏了更深层次的数据问题。本文将通过一些示例来探讨DISTINCT的正确使用方式,以及如何避免使用它来掩盖问题。
DISTINCT关键字用于返回唯一不同的值。例如,如果有一个包含重复值的表,使用DISTINCT可以帮助筛选出不重复的记录。
CREATE TABLE testDistinct (
id INT
);
INSERT INTO testDistinct VALUES (1), (2), (1), (3), (4), (1), (3), (2), (5);
SELECT * FROM testDistinct;
SELECT DISTINCT id FROM testDistinct;
如上代码所示,第二次查询使用了DISTINCT关键字,从而去除了重复的id值。
假设正在为汽车修理店管理一个邮件服务器。有修理工、客户、客户的汽车以及每个修理工的邮件设置的表格。
CREATE TABLE autoRepair (
id INT IDENTITY,
name VARCHAR(50)
);
CREATE TABLE client (
id INT IDENTITY,
repairerId INT,
name VARCHAR(50)
);
CREATE TABLE car (
id INT IDENTITY,
clientId INT,
type VARCHAR(50)
);
CREATE TABLE repairMailSettings (
repairId INT,
replyAddress VARCHAR(100)
);
INSERT INTO autoRepair VALUES ('CO-OP Toyota'), ('Motors Holden');
INSERT INTO client VALUES (1, 'Fred Smith'), (1, 'Joe Kaputnuk'), (2, 'Bill Brown');
INSERT INTO car VALUES (1, 'Fred''s car'), (1, 'Fred''s other car'), (2, 'The Joe Mobile'), (3, 'Bill''s Brown Bomb');
INSERT INTO repairMailSettings VALUES (1, 'info@co-op.com'), (2, 'help@motors.com'), (1, 'info@co-op.com');
如果想要获取所有汽车拥有者及其汽车和修理工的列表,可以执行以下查询:
SELECT ar.name AS RepairerName, c.Name AS ClientName, cr.Type AS CarName
FROM autoRepair ar
INNER JOIN client c ON c.repairerId = ar.id
INNER JOIN car cr ON cr.clientId = c.id;
但是,如果想要包括邮件设置,就需要执行另一个查询:
SELECT ar.name AS RepairerName, c.Name AS ClientName, cr.Type AS CarName, ms.replyAddress
FROM autoRepair ar
INNER JOIN client c ON c.repairerId = ar.id
INNER JOIN car cr ON cr.clientId = c.id
INNER JOIN repairMailSettings ms ON ms.repairId = ar.id;
这个查询返回了所有记录,包括重复的邮件地址。为了解决这个问题,可能会想到使用DISTINCT:
SELECT DISTINCT ar.name AS RepairerName, c.Name AS ClientName, cr.Type AS CarName, ms.replyAddress
FROM autoRepair ar
INNER JOIN client c ON c.repairerId = ar.id
INNER JOIN car cr ON cr.clientId = c.id
INNER JOIN repairMailSettings ms ON ms.repairId = ar.id;
虽然这看起来解决了问题,但实际上可能只是隐藏了问题。
JOIN操作符通常用于定义一对多的关系。例如,一个客户可能拥有多辆汽车,但在客户表中,他的名字只出现一次。当将客户表与汽车表进行连接时,由于他有两辆汽车,会创建两行,名字相同但汽车名称不同。
有时候,人们使用DISTINCT是因为他们没有提出正确的问题。例如,有人可能会问为什么以下两个查询的结果不同:
SELECT COUNT(a.id) FROM a INNER JOIN b ON a.id = b.id;
SELECT COUNT(a.id) FROM a WHERE a.id NOT IN (SELECT id FROM b);
原因是b表中的id可能不止一次出现,这在连接时创建了重复的行,从而增加了计数。虽然JOIN总是比子查询更好,但有时候JOIN可能没有提出正确的问题。