我如何事先意识到我需要在查询中使用DISTINCT?

时间:2015-12-24 16:33:02

标签: sql

假设我遇到如下SQL问题: enter image description here

我编写了以下SQL查询:

SELECT BARS.bar
FROM SELLS JOIN
     BARS
     ON BARS.bar = SELLS.bar JOIN
     DRINKS
     ON DRINKS.drink = SELLS.drink
WHERE BARS.address = 'Nowowiejska' AND
      DRINKS.type = 'Mineral Water' AND
      SELLS.price < 3

但是,在对MS SQL Server中实现的真实数据库运行查询后,我发现有一些重复的条形名称。所以,我使用DISTINCT修复了我的查询。但是,在实际数据库中实际实现查询之前,我无法实现重复。

我的问题是,我如何才能意识到我需要在查询中使用DISTINCT?

4 个答案:

答案 0 :(得分:2)

一般来说,您只需要了解表格之间关系的基数。在您的示例中,如果您想要一个每个条最多包含一个记录的结果集,那么您需要知道将BARS表连接到可能包含单个条的多个记录的任何其他表(例如{ {1}})也可能为结果集中的同一个条形成多个记录。

那就是说,我非常同意Gordon Linoff对你的问题的评论:如果你正确地构建你的联接,我怀疑你几乎不必使用SELLS。我写了相当数量的SQL并且我使用DISTINCT很少,当我看到它时,我会仔细查看查询,看看它是否真的需要或是否被用作“黑客”来掩盖一些不正确的连接。

有一个名为半连接的东西对于你正在处理的问题很有用:你想在哪里查询某些表(DISTINCT)以查看是否有某些特定的数据存在但实际上不需要返回它。这是在SQL Server中通过关键字SELLS实现的。以下是如何将其用于解决问题的示例:

EXISTS

您可以阅读关于加入here的精彩介绍以及有关-- Sample data from the question: declare @Bars table (Bar varchar(32), [Address] varchar(32)); declare @Drinks table (Drink varchar(32), [Type] varchar(32)); declare @Sells table (Bar varchar(32), Drink varchar(32), Price money); insert @Bars values ('A', 'Nowowiejska'), ('B', 'Oak Creek'), ('C', 'Greenfield'); insert @Drinks values ('San Pellegrino', 'Mineral Water'); insert @Sells values ('B', 'San Pellegrino', 2.99), ('C', 'San Pellegrino', 3.50); -- List bars whose address is Nowowiejska or which sell mineral water for < $3. select B.Bar from @Bars B where B.[Address] = 'Nowowiejska' or exists ( select 1 from @Drinks D inner join @Sells S on D.Drink = S.Drink where S.Bar = B.Bar and D.[Type] = 'Mineral Water' and S.Price < 3 ); here的更多内容。

答案 1 :(得分:2)

要知道您是否需要使用DISTINCT,您需要知道您的连接是否会产生重复,这意味着您需要了解它们的工作原理。

首先,您需要更仔细地阅读问题。它要求在'Nowowiejska'街道上的酒吧 AND 酒吧出售'矿泉水'为&lt; 3.由于您只在查询中使用AND,因此您只能获得“Nowowiejska”街道上的酒吧 AND 出售“矿泉水”&lt; 3。

以下是您的查询应如下所示:

SELECT DISTINCT Sells.bar
FROM Sells
LEFT OUTER JOIN Bars
    ON Sells.bar = Bars.bar
LEFT OUTER JOIN DRINKS 
    ON Sells.drink = Drinks.drink
WHERE Bars.address = 'Nowowiejska'
OR 
    (
        Drinks.type = 'Mineral Water'
        AND
        Sells.price < 3
    )

请注意where块的结构 - 这将允许出现在'Nowowiejska'街道 AND 栏上的 BOTH 栏,其中出售'矿泉水'为&lt; 3。

由于酒吧可能会有一个'Nowowiejska' AND 的地址,以便少于3的'矿泉水'饮料,您需要允许 BOTH 可能性。通过使用左外连接,您将获得所有酒吧,所有地址和所有饮料类型和价格。然后,where子句将结果集过滤到所需的条件。最后,DISTINCT确保当一个条与两个连接匹配时,您只能获得一次。

简而言之,当“命中”可能匹配由OR分隔的多个条件时,或者当“命中”可能匹配其中一个连接表中的多个记录时,请使用DISTINCT。由于一个酒吧在一条街上不能有多个存储的地址 - 并且由于一个酒吧不能存储多个相同的饮料(如果其中任何一个都是真的,你应该立即解雇你的DBA和/或开发人员),你赢了'从各个连接中获取多个记录。然而,完全有可能酒吧可以在所需的街道上,并以低于期望的价格提供所需的饮料 - 并且您不希望这些酒吧返回两次。

我希望这会有所帮助,如果您需要澄清,请随时发表评论。

修改

也可以简单地将两个(基本上是单独的)查询与union结合起来。我建议不要这样做,因为最好在可能的情况下整合查询,但我认为包括这可能有助于您更好地理解联接的工作方式。

SELECT Sells.bar
FROM Sells
JOIN Bars 
    ON Sells.bar = Bars.bar
WHERE Bars.address = 'Nowowiejska'

UNION

SELECT Sells.bar
FROM Sells
JOIN Drinks
    ON Sells.bar = Drinks.bar
WHERE Drinks.type = 'Mineral Water'
AND Sells.price < 3

请注意使用

UNION ALL

使用

保留重复项
UNION

没有。

答案 2 :(得分:1)

了解您在查询中是否需要不同的方法(IMO不常见)的方法是了解限制表中行的唯一性,并从中了解您的连接相对于该唯一性的影响。

示例:如果我从条形表中选择条形并且表格将它们限制为唯一条形,那么根据定义,我从不需要DISTINCT用于该选择。

但是,如果我将该集合加入另一个表,则连接逻辑会进入问题,我必须了解连接对生成的值的影响。

最后,将实际连接(tablea inner join tableb on ...)的概念与存在检查(即半连接(from tablea where exists ( select * from tableb ...))分开。对于开始编写内部联接的人来说,这是非常常见的,它会获取所有匹配,也许他们只需要检查行是否存在,哪些不存在。如果您依赖内部联接,那么您将获得比您可能需要的行更多的行,并且可能最终使用DISTINCT作为解决方法 - 尽管EXISTS表现更好,并且首先不需要DISTINCT。

例如,出售矿泉水的酒吧可能类似于bars where exists ( select * from drinks ... where <some criteria> )

除此之外:count()也是exists()的不良替代品,在很多情况下,只是为了测试是否有匹配的行。

答案 3 :(得分:-3)

  

我的问题是,我如何才能意识到我需要在查询中使用DISTINCT?

IF EXISTS(
SELECT BARS.bar
FROM SELLS, BARS, DRINKS
WHERE  BARS.bar = SELLS.bar
AND DRINKS.drink = SELLS.drink
AND BARS.address = 'Nowowiejska'
AND DRINKS.type = 'Mineral Water'
AND SELLS.price < 3
GROUP BY BARS.bar
HAVING COUNT(*) > 1
)
SELECT DISTINCT_OR_NOT_DISTINCT ='You need DISTINCT here'
ELSE
SELECT DISTINCT_OR_NOT_DISTINCT ='You dont need DISTINCT here'