【问题标题】:How to join SQL statements with different where clause on different column of same table如何在同一张表的不同列上使用不同的where子句连接SQL语句
【发布时间】:2018-07-04 20:30:49
【问题描述】:

我在 sql 中有 3 个查询:

第一个查询:

select t1ID ,AVG(t2score) AS AVG1
from T1
WHERE t1m1 NOT IN (t2m1,t2m2,t2m3) and t1m2 IN (t2m1,t2m2,t2m3)
group by t1ID

结果

+------+------+
| t1ID | AVG1 |
+------+------+
|    1 |   55 |
|    2 |   45 |
|    3 |   73 |
|    4 |   69 |
+------+------+

第二次查询:

select t1ID ,AVG(t2score) AS AVG2
from T1
WHERE t1m2 NOT IN (t2m1,t2m2,t2m3) and t1m1 IN (t2m1,t2m2,t2m3)
group by t1ID

结果

+------+------+
| t1ID | AVG2 |
+------+------+
|    1 | 68   |
|    2 | 56   |
|    3 | NULL |
|    4 | NULL |
+------+------+

第三次查询

select t1ID ,AVG(t2score) AS AVGt3
from T1
WHERE t1m3 NOT IN (t2m1,t2m2,t2m3) and t1m1 IN (t2m1,t2m2,t2m3)
group by t1ID

结果

+------+------+
| t1ID | AVG3 |
+------+------+
|    1 | NULL |
|    2 | 70   |
|    3 | NULL |
|    4 | NULL |
+------+------+

如何组合这三个语句,以便像这样将这些结果加在一起(每个 AVG 分数在不同的列中)

期望的结果:

+------+------+------+------+
| t1ID | AVG1 | AVG2 | AVG3 |
+------+------+------+------+
|    1 |   55 | 68   | NULL |
|    2 |   45 | 56   | 70   |
|    3 |   73 | NULL | NULL |
|    4 |   69 | NULL | NULL |
+------+------+------+------+

【问题讨论】:

  • 使用case表达式进行条件聚合。
  • 你的 IN 子句的内容是文字还是列标识符?

标签: sql sql-server ssms


【解决方案1】:

cmets 中提到的条件聚合通过在 AVG() 函数调用中放置 CASE 表达式来工作,因此仅对满足特定条件的值求平均值...

SELECT
   t1ID,
   AVG(CASE WHEN t1m1 NOT IN (t2m1,t2m2,t2m3) and t1m1 IN (t2m1,t2m2,t2m3) THEN t2Score END)   AVG1,
   AVG(CASE WHEN t1m2 NOT IN (t2m1,t2m2,t2m3) and t1m1 IN (t2m1,t2m2,t2m3) THEN t2Score END)   AVG2,
   AVG(CASE WHEN t1m3 NOT IN (t2m1,t2m2,t2m3) and t1m1 IN (t2m1,t2m2,t2m3) THEN t2Score END)   AVG3
FROM
    T1
GROUP BY
    t1ID

这是可行的,因为任何不符合 CASE 表达式中的条件的东西都会返回 NULL,并且所有聚合函数(例如 AVG())实际上都会忽略 NULLs。

【讨论】:

  • 案例表达式,不是语句。
  • @Jodrell - 这是对整个表的单次扫描。另一种方法是对表格的各个部分进行三次扫描。如果这些部分易于识别(索引)明显小于表的 1/3,那么单独的查询可以 更快。在这种情况下,我会UNION 并聚合三个查询的结果,而不是JOIN 它们。
  • 经过思考,我强烈怀疑这是最好的方法。子查询或者UNIONS肯定比LEFT JOINs好,对吧?
  • @MatBailie 我同意,您的回答更适合一般情况。
【解决方案2】:

您可以使用 CTE 来实现输出。像这样-

WITH cte1 AS
(
    select t1ID ,AVG(t2score) AS AVG1
    from T1
    WHERE t1m1 NOT IN (t2m1,t2m2,t2m3) and t1m2 IN (t2m1,t2m2,t2m3)
    group by t1ID

 ),
 cte2 AS
 (
    select t1ID ,AVG(t2score) AS AVG2
    from T1
    WHERE t1m2 NOT IN (t2m1,t2m2,t2m3) and t1m1 IN (t2m1,t2m2,t2m3)
    group by t1ID
 ),
 cte3 as
 (
     select t1ID ,AVG(t2score) AS AVGt3
     from T1
     WHERE t1m3 NOT IN (t2m1,t2m2,t2m3) and t1m1 IN (t2m1,t2m2,t2m3)
     group by t1ID
)
SELECT cte1.t1ID, cte1.AVG1, cte2.AVG2, cte3.AVG3
FROM cte1
JOIN cte2 ON cte1.t1ID = cte2.t1ID
JOIN cte3 ON cte2.t1ID = cte3.t1ID

如果t1ID 列总是返回每个查询中的所有 ID,这是合适的。

换句话说(截至MatBailie

这仅适用于子查询中的 WHERE 子句从不 过滤掉任何必要的t1ID 值。

【讨论】:

  • 您的警告最好说:这仅适用于子查询中的 WHERE 子句从不过滤掉任何必需的 t1ID 值。 (重要的是WHERE 子句的行为...)
  • 另外,请不要使用cte1, cte2, cte2,请实际使用JOIN 表示法。
  • 为什么偏爱 CTE 而不是子查询?
  • 这个想法没问题。但是,逗号分隔的连接在 1992 年变得多余。请不要再使用它们了。查询正常工作真正需要的是完整的外部联接。但是,由于缺少 USING 子句,在 SQL Server 中对同一列进行多个完全外连接有点笨拙。
  • @Jodrell:在这种情况下,性能是相同的。它更具可读性。
【解决方案3】:

您可以只对 id 执行 SELECT,然后对 id 执行 (LEFT) JOIN 子查询,如下所示:

select t1ID, AVG1, AVG2, AVG3 FROM 
(SELECT t1ID FROM T1 GROUP BY t1ID) AS IDs
JOIN (select t1ID ,AVG(t2score) AS AVG1
from T1
WHERE t1m1 NOT IN (t2m1,t2m2,t2m3) and t1m2 IN (t2m1,t2m2,t2m3)
group by t1ID) AS group1 USING (t1ID)
JOIN (select t1ID ,AVG(t2score) AS AVG2
from T1
WHERE t1m2 NOT IN (t2m1,t2m2,t2m3) and t1m1 IN (t2m1,t2m2,t2m3)
group by t1ID) AS group2 USING (t1ID)
JOIN (select t1ID ,AVG(t2score) AS AVG3
from T1
WHERE t1m3 NOT IN (t2m1,t2m2,t2m3) and t1m1 IN (t2m1,t2m2,t2m3)
group by t1ID) AS group3 USING (t1ID)

你必须给每个子查询一个表别名。

【讨论】:

  • 将所有结果加入t1 表假定t1IDt1 表中是唯一的。如果是这样的话,AVG() 将毫无意义,因为它只处理单行......
  • 你说得对,外面的语句里肯定也有group。
  • 现在你有一个语法错误,因为你选择了AVG1,但既没有按它分组,也没有聚合它。最好使用(SELECT t1ID FROM t1 GROUP BY t1ID) AS t1 JOIN ...
  • 我看不出哪里有语法错误。当然,我可以选择我不分组的东西。但是您的方法具有更好的性能。
  • 不,你不能。 SELECT a, b, c, d FROM x GROUP BY a 是语法错误。 SELECT a, b, c, d FROM x GROUP BY a, b, c, d 有效,SELECT a, MAX(b), MAX(c), MAX(d) FROM x GROUP BY a 有效。自己尝试一下(即使是现在使用默认设置的 MySQL 也会抛出错误,而 SQL Server 会总是抛出错误。)
【解决方案4】:
{SELECT t1ID, AVG(FirstT.t2score) AS AVG1, AVG(SecondT.t2score) AS AVG1, AVG(ThirdT.t2score) AS AVG1
FROM T1 t
JOIN T1 FirstT ON FirstT.t1ID = t.t1ID AND FirstT.t1m1 NOT IN (t2m1,t2m2,t2m3) AND FirstT.t1m2 IN (t2m1,t2m2,t2m3)
JOIN T1 SecondT ON SecondT.t1ID = t.t1ID AND SecondT.t1m2 NOT IN (t2m1,t2m2,t2m3) AND SecondT.t1m1 IN (t2m1,t2m2,t2m3)
JOIN T1 ThirdT ON ThirdT.t1ID = t.t1ID AND FirstT.t1m3 NOT IN (t2m1,t2m2,t2m3) and t1m1 IN (t2m1,t2m2,t2m3)enter code here
GROUP BY t1ID}

【讨论】:

  • 虽然此代码可以回答问题,但提供有关 如何为什么 解决问题的附加上下文将提高​​答案的长期价值。
【解决方案5】:

创建临时表并向其中插入数据可能会对您有所帮助

【讨论】:

  • 这是一个评论而不是一个答案,也不是一个非常有建设性的评论,除非你知道我不知道的事情。
猜你喜欢
  • 2020-08-18
  • 1970-01-01
  • 2018-08-30
  • 2021-10-20
  • 2019-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多