【问题标题】:SQL Select rows with duplicate data across multiple columnsSQL 选择跨多列具有重复数据的行
【发布时间】:2015-09-28 15:49:26
【问题描述】:

如果我有一个包含名字、姓氏(复合唯一)和外卖选择的表格,例如

+------+-------+---------+
|First | Last  | Food    |
+------+-------+---------+
|Bob   | Smith | Pizza   |
|Bob   | Smith | Chips   |
|Jim   | Smith | Pizza   |
|Lisa  | Jones | Pizza   |
|Lisa  | Jones | Chinese |
|Lisa  | James | Mexican |
|Eric  | White | Chinese |
|Eric  | White | Chips   |
+------+-------+---------+

我想要与至少有两种食物偏好的人对应的所有行,其中之一是披萨。即

的结果
+------+-------+---------+
|First | Last  | Food    |
+------+-------+---------+
|Bob   | Smith | Pizza   |
|Bob   | Smith | Chips   |
|Lisa  | Jones | Pizza   |
|Lisa  | Jones | Chinese |
+------+-------+---------+

我已经尝试通过 ... count(*) > 1 加入... group,但它并不能完全按照我的意愿工作。

任何帮助表示赞赏,谢谢。

【问题讨论】:

  • 你需要的是先分组,最后分组,然后有食物计数> 1
  • 请注意,您有两个答案。由于您同时标记了 sql-server 和 mysql,因此响应会有所不同。您应该标记适当的 DBMS,因为它们不是一回事。此外,您确实应该研究标准化,因为您发布的内容未标准化。对名字和姓氏组合的唯一约束是一个糟糕的设计,因为它会阻止两个人使用相同的名字。
  • “Eric”不应该也出现在您的预期结果中吗?
  • @McAdam331,我认为 OP 不会希望 Eric 返回,因为披萨不是食物偏好之一。我相信 OP 希望有超过 1 种食物偏好的人,其中一种偏好是披萨。
  • @ander2ed 很好。我相应地编辑了我的答案。

标签: mysql sql-server


【解决方案1】:

另一种方式:SQL Server 2012+ 中的P。

SELECT FIRST,LAST,FOOD FROM(
SELECT A.FIRST,A.LAST,B.FOOD,
COUNT(*) OVER (PARTITION BY A.FIRST,A.LAST ORDER BY A.FIRST,A.LAST DESC) AS 'POS'
FROM TEST_FOOD A
INNER JOIN TEST_FOOD B
ON A.FIRST = B.FIRST
AND A.LAST = B.LAST
WHERE A.FOOD = 'PIZZA') TB WHERE POS = 2

在 SQL Server 2005+ 中还有另一种更标准的方式,第一种方式更快,但受 MSSQL 版本限制。

SELECT C.FIRST,C.LAST,C.FOOD FROM 
(
SELECT A.FIRST,A.LAST FROM TEST_FOOD A
INNER JOIN TEST_FOOD B
ON A.FIRST = B.FIRST
AND A.LAST = B.LAST
WHERE B.FOOD = 'PIZZA'
GROUP BY A.FIRST,A.LAST
HAVING COUNT(*) = 2
) TB INNER JOIN 
TEST_FOOD C
ON TB.FIRST = C.FIRST
AND TB.LAST = C.LAST

【讨论】:

    【解决方案2】:

    您可以从获取多次出现的名字和姓氏列表开始:

    SELECT first, last
    FROM myTable
    GROUP BY first, last
    HAVING COUNT(*) > 1;
    

    一旦你有了它,你就可以将它加入你的原始表并确保你只选择这些名称:

    SELECT m.*
    FROM myTable m
    JOIN(
       SELECT first, last
       FROM myTable
       GROUP BY first, last
       HAVING COUNT(*) > 1) tmp ON tmp.first = m.first AND tmp.last = m.last;
    

    不过,此查询的问题在于,它无法验证该人的至少一种首选食物是披萨。为此,我们可以在第一个子查询的 HAVING 子句中添加一个附加条件来检查一行披萨。我使用条件聚合来做到这一点:

    SELECT m.*
    FROM myTable m
    JOIN(
       SELECT first, last
       FROM myTable
       GROUP BY first, last
       HAVING COUNT(*) > 1 AND SUM(food = 'Pizza') = 1) tmp ON tmp.first = m.first AND tmp.last = m.last;
    

    这是一个SQL Fiddle 示例。

    【讨论】:

      【解决方案3】:
      ;WITH CTE AS (
      SELECT FIRST, LAST, MAX(FOOD) AS FOOD, COUNT(*) AS COUNT
      FROM TABLE1
      GROUP BY FIRST, LAST
      HAVING COUNT(*) >= 2)
      SELECT *
      FROM TABLE1
      WHERE FIRST+LAST IN (SELECT FIRST+LAST FROM CTE)
      

      有很多方法可以做到这一点。这只是一种方式。

      【讨论】:

      • OP 还想确保首选食物之一是比萨饼,这个查询将拉出至少有两种首选食物的人,无论它是否包括比萨饼(抱歉复制粘贴评论 @McAdam331 )
      【解决方案4】:
      select first,last,GROUP_CONCAT(food separator ',') 
      from people 
      group by first,last 
      having count(food) > 1 AND SUM(food = 'Pizza') = 1
      

      组 concat 将以逗号分隔的字符串带回食物。

      +------+-------+---------------+
      |First | Last  | Food          |
      +------+-------+---------------+
      |Bob   | Smith | Pizza,Chips   |
      |Lisa  | Jones | Pizza,Chinese |
      +------+-------+---------------+
      

      【讨论】:

      • OP 还想确保首选食物之一是比萨饼,此查询将拉出至少有两种首选食物的人,无论它是否包括比萨饼。
      【解决方案5】:

      或者类似的东西(使用 McAdam331 的小提琴)...

       SELECT DISTINCT y.*
                  FROM mytable x
                  JOIN mytable y
                    ON y.first = x.first
                   AND y.last = x.last
                  JOIN mytable z
                    ON z.first = x.first
                   AND z.last = x.last
                   AND z.food <> y.food
                 WHERE x.food = 'pizza';
      

      【讨论】:

        猜你喜欢
        • 2013-02-05
        • 1970-01-01
        • 1970-01-01
        • 2016-07-22
        • 2020-11-11
        • 1970-01-01
        • 2011-12-06
        相关资源
        最近更新 更多