SQL SERVER 查询中的 UNION

Posted

技术标签:

【中文标题】SQL SERVER 查询中的 UNION【英文标题】:UNION in SQL SERVER query 【发布时间】:2014-03-18 01:37:11 【问题描述】:

我是 SQL SERVER 的初学者。我有这 2 个查询:

第一个查询:

SELECT 'OK' AS STATUS, COUNT(*) AS BIL 
FROM 
(SELECT D.DAJ_NODAFTAR 
FROM BATM112_DES_AMJENTERA AS D 
WHERE D.DAJ_KODKAT='VTC' 
AND (SELECT TOP 1 DAJ_KLASIFIKASI FROM BATM112_RKDKLASIFIKASI WHERE     
DAJ_NODAFTAR=D.DAJ_NODAFTAR ORDER BY DAJ_TKHDE DESC )='OK') D

第二次查询:

SELECT 'NOT OK' AS STATUS, COUNT(*) AS BIL 
FROM 
(SELECT D.DAJ_NODAFTAR 
FROM BATM112_DES_AMJENTERA AS D 
WHERE D.DAJ_KODKAT='VTC' 
AND (SELECT TOP 1 DAJ_KLASIFIKASI FROM BATM112_RKDKLASIFIKASI WHERE     
DAJ_NODAFTAR=D.DAJ_NODAFTAR ORDER BY DAJ_TKHDE DESC )='NOT OK') D

当我尝试运行这两个查询时,结果完美显示,但是当我尝试使用 UNION 组合这两个查询时,如下所示:

SELECT 'OK' AS STATUS, COUNT(*) AS BIL 
FROM 
(SELECT D.DAJ_NODAFTAR 
FROM BATM112_DES_AMJENTERA AS D 
WHERE D.DAJ_KODKAT='VTC' 
AND (SELECT TOP 1 DAJ_KLASIFIKASI FROM BATM112_RKDKLASIFIKASI WHERE     
DAJ_NODAFTAR=D.DAJ_NODAFTAR ORDER BY DAJ_TKHDE DESC )='OK') D
UNION
SELECT 'NOT OK' AS STATUS, COUNT(*) AS BIL 
FROM 
(SELECT D.DAJ_NODAFTAR 
FROM BATM112_DES_AMJENTERA AS D 
WHERE D.DAJ_KODKAT='VTC' 
AND (SELECT TOP 1 DAJ_KLASIFIKASI FROM BATM112_RKDKLASIFIKASI WHERE     
DAJ_NODAFTAR=D.DAJ_NODAFTAR ORDER BY DAJ_TKHDE DESC )='NOT OK') D

出现此错误消息:

消息 104,第 15 级,状态 1,第 1 行 如果语句包含 UNION 运算符,则 ORDER BY 项必须出现在选择列表中。

谁能告诉我如何解决这个错误?

【问题讨论】:

你能在 UNION 之后发布完整的查询吗? 好的。我已经在联合后发布了完整的查询。检查我上面的问题。 【参考方案1】:

您应该能够首先使用窗口化 CTE 来消除子查询中对顺序的需要。字段的复杂性使得我没有构建示例数据来验证,但类似于:

WITH cte AS (
 SELECT DAJ_KLASIFIKASI
       ,DAJ_NODAFTAR
       ,ROW_NUMBER() OVER (PARTITION BY DAJ_NODAFTAR ORDER BY DAJ_TKHDE DESC)
   FROM BATM112_RKDKLASIFIKASI
)
SELECT 'OK' AS STATUS,
       COUNT(*) AS BIL
FROM   (SELECT D.DAJ_NODAFTAR
        FROM   BATM112_DES_AMJENTERA AS D
        WHERE  D.DAJ_KODKAT = 'VTC'
               AND (SELECT DAJ_KLASIFIKASI FROM cte WHERE DAJ_NODAFTAR = D.DAJ_NODAFTAR AND rn=1) = 'OK') AS D
UNION
SELECT 'NOT OK' AS STATUS,
       COUNT(*) AS BIL
FROM   (SELECT D.DAJ_NODAFTAR
        FROM   BATM112_DES_AMJENTERA AS D
        WHERE  D.DAJ_KODKAT = 'VTC'
               AND (SELECT DAJ_KLASIFIKASI FROM cte WHERE DAJ_NODAFTAR = D.DAJ_NODAFTAR AND rn=1) = 'NOT OK') AS D

sql-server-order-by-in-subquery-with-union 也有类似的讨论。

【讨论】:

我使用的是 SQL SERVER 2000。这种类型的查询在这个版本中可能无法识别,因为当我尝试运行时,会出现此错误消息:Incorrect syntax near the keyword 'WITH'. 'ROW_NUMBER' is not a recognized function name. 哦,是的,这完全不适用于 2000。将来,如果您使用适当的版本进行标记,它会对每个人都有帮助。【参考方案2】:

错误信息已经指出了答案。只需删除查询中的“ORDER BY DAJ_TKHDE DESC”。这就对了!然后再次运行联合查询。

SELECT 'OK' AS STATUS, COUNT(*) AS BIL 
FROM 
(SELECT D.DAJ_NODAFTAR 
FROM BATM112_DES_AMJENTERA AS D 
WHERE D.DAJ_KODKAT='VTC' 
AND (SELECT TOP 1 DAJ_KLASIFIKASI FROM BATM112_RKDKLASIFIKASI WHERE     
DAJ_NODAFTAR=D.DAJ_NODAFTAR)='OK') D

UNION

SELECT 'NOT OK' AS STATUS, COUNT(*) AS BIL 
FROM 
(SELECT D.DAJ_NODAFTAR 
FROM BATM112_DES_AMJENTERA AS D 
WHERE D.DAJ_KODKAT='VTC' 
AND (SELECT TOP 1 DAJ_KLASIFIKASI FROM BATM112_RKDKLASIFIKASI WHERE     
DAJ_NODAFTAR=D.DAJ_NODAFTAR)='NOT OK') D

更新

提供第二种解决方案(替代方法),

declare @MyTable table
(
    Status varchar(25),
    Count int
)

insert into @MyTable
your first query

insert into @MyTable
your second query

select * from @MyTable

【讨论】:

这会改变内部查询的逻辑。 @Szymon 你是对的。另一种方法是声明表变量,并将第一次和第二次查询的内容加载到表中。但是,这不是用户正在寻找的“联合”解决方案。 并不是你想的那么简单。如果我删除两个ORDER BY DAJ_TKHDE DESC,是的,结果显示没有错误消息,但结果是错误的。计数不正确。我使用这个(SELECT TOP 1 DAJ_KLASIFIKASI FROM BATM112_RKDKLASIFIKASI WHERE DAJ_NODAFTAR=D.DAJ_NODAFTAR) 来确保当它计算时,我不会包含相同DAJ_NODAFTAR 的重复数据。 @user3106393 我的错。但是,为了解决您的问题,您可以使用其他替代方法,例如使用表变量吗?还是必须使用 UNION? @user3106393 我将它添加到我的帖子内容中。让我知道您有任何问题。【参考方案3】:

您需要重新排列查询。 我对以下查询的目标是将子查询值(在表变量中具有“排序依据”的查询)保留在表变量中,并且表变量将用“排序依据”替换您的子查询

Declare @temp Table
(
 DAJ_KLASIFIKASI nvarchar(50)
)

Insert into @temp values
SELECT TOP 1 DAJ_KLASIFIKASI FROM BATM112_RKDKLASIFIKASI WHERE   
DAJ_NODAFTAR in ( SELECT D.DAJ_NODAFTAR FROM BATM112_DES_AMJENTERA AS D WHERE D.DAJ_KODKAT='VTC') and DAJ_Klasifikasi = 'NOT OK' ORDER BY DAJ_TKHDE DESC 

Insert into @temp values
SELECT TOP 1 DAJ_KLASIFIKASI FROM BATM112_RKDKLASIFIKASI WHERE   
DAJ_NODAFTAR in (SELECT D.DAJ_NODAFTAR FROM BATM112_DES_AMJENTERA AS D WHERE D.DAJ_KODKAT='VTC') and DAJ_Klasifikasi = 'OK' ORDER BY DAJ_TKHDE DESC 

您修改后的查询:

SELECT 'OK' AS STATUS, COUNT(*) AS BIL 
FROM 
(SELECT D.DAJ_NODAFTAR 
FROM BATM112_DES_AMJENTERA AS D 
WHERE D.DAJ_KODKAT='VTC' 
AND ((SELECT DAJ_KLASIFIKASI FROM @temp where DAJ_Klasifikasi='OK')='OK') D
UNION
SELECT 'NOT OK' AS STATUS, COUNT(*) AS BIL 
FROM 
(SELECT D.DAJ_NODAFTAR 
FROM BATM112_DES_AMJENTERA AS D 
WHERE D.DAJ_KODKAT='VTC' 
AND ((SELECT DAJ_KLASIFIKASI FROM @temp where DAJ_Klasifikasi='NOT OK')='NOT OK') D

注意:此查询未经测试,因为我没有示例数据。所以请在使用前彻底测试。您需要在主查询中删除订单。

【讨论】:

没有。 BATAM 不代表 BATAM。与此无关【参考方案4】:

你有没有可能用一个不必要的联合把它复杂化了?我在跟踪表/字段名称时遇到了一些麻烦,但我认为您要做的就是冒泡 OK 或 NOT OK 并计算出现次数。也许用 GROUP BY 代替?

SELECT STATUS,
       COUNT(*) AS BIL
  FROM (SELECT D.DAJ_NODAFTAR
              ,(SELECT TOP 1 
                       DAJ_KLASIFIKASI
                  FROM BATM112_RKDKLASIFIKASI
                 WHERE DAJ_NODAFTAR = D.DAJ_NODAFTAR
                 ORDER BY DAJ_TKHDE DESC) STATUS
          FROM BATM112_DES_AMJENTERA AS D
         WHERE D.DAJ_KODKAT = 'VTC') a
 GROUP BY STATUS

【讨论】:

【参考方案5】:

只需从第一个选择查询中删除订单即可。使用联合时,这是不允许的。所以,你得到了这个错误。

使用联合时,只能在最后一个(这里是第二个)选择查询中使用 order by 子句。

如果您的需求也确实需要在第一个查询中排序,可以在这里找到解决方案。

SQL Query - Using Order By in UNION

【讨论】:

以上是关于SQL SERVER 查询中的 UNION的主要内容,如果未能解决你的问题,请参考以下文章

sql中的高手中的高手来看看这个SQL查询语句中的union错在哪儿了?

使用多个 UNION 重写 SQL Server 查询

SQL Server 查询:Union vs Distinct union 所有性能

SQL Server-聚焦UNIOL ALL/UNION查询

SQL Server 是不是可以使用 PHP 从 UNION 查询中返回不同的别名列名?

sql server 中union的用法?