根据可变大小的数据集的内容返回单个值

时间:2015-05-04 13:46:53

标签: sql sql-server sql-server-2008-r2

我正在查询数据库中的用户信息。我特别感兴趣的是从名为CONTENTS的字段中获取数据,但仅限于给定DESCRIPTION的匹配USERID。另外,我不想返回CONTENTS,而是想要解析它们中的一组。返回的行数可以是可变的,从0到非常大的量。 CONTENTS字段始终为一个字符或无字符。根据是否在集合中找到某个字符,我将返回不同的值。不,WHERE子句在这里没有帮助,因为我只想要一个结果,无论返回的行数是多少。

  • 如果集合中有数据:
    • 如果数据包含N,空格或空白以外的字符,请返回*
    • 否则:返回X
  • 否则:返回_

我有一个有效的查询,但它很大并且在其中多次复制类似的SQL段:

IF(
    SELECT COUNT(*)
    FROM TABLE
    WHERE USERID = '123456789'
    AND DESCRIPTION IN ('D1','D2','D3')
    AND CONTENTS NOT IN ('N','',' ')
    ) > 0
    SELECT '*'
ELSE
IF(
    SELECT COUNT(*)
    FROM TABLE
    WHERE USERID = '123456798'
    AND DESCRIPTION IN ('D1','D2','D3')
    ) > 0
    SELECT 'X'
ELSE
    SELECT '_'

正如您所看到的,我复制WHERE条款两次,我不喜欢这样。我理想情况下只想看一次WHERE条款。我试图简化这个,我得到了这个,但问题是它返回了多行:

SELECT
    CASE
        WHEN COUNT(*) > 0 THEN
            CASE
                WHEN C.CONTENTS = 'N' THEN 'X'
                WHEN C.CONTENTS = ' ' THEN 'X'
                WHEN C.CONTENTS = '' THEN 'X'
                ELSE '*'
            END
        ELSE '_'
    END AS 'RESULT'
FROM TABLE C
WHERE
    USERID = '123456789' AND
    DESCRIPTION IN ('D1','D2','D3')
GROUP BY C.CONTENTS

我相信窗口功能可能对我有所帮助,但是我在这个场景中遇到很多麻烦。是否有可能有一个不重复WHERE子句的查询可以得到我想要的结果,只返回一个结果而不管行数?

2 个答案:

答案 0 :(得分:1)

您可以使用CASE WHEN ... END内的COUNT(它不会计算NULL项目),并按USERID进行分组:

SELECT USERID, CASE WHEN STAR > 0 THEN '*' WHEN X > 0 THEN 'X' ELSE '_' END
FROM (
    SELECT USERID, 
        COUNT(CASE WHEN DESCRIPTION IN ('D1','D2','D3') AND CONTENTS NOT IN ('N','',' ') THEN 1 ELSE NULL END) AS STAR,
        COUNT(CASE WHEN DESCRIPTION IN ('D1','D2','D3') THEN 1 ELSE NULL END) AS X
     FROM TABLE
     GROUP BY USERID
     ) AS x
WHERE USERID = '123456789'

我现在没有SQL Server,所以我不确定它是否可以在没有匿名视图的情况下完成。你可以尝试一下:

SELECT  CASE
            WHEN COUNT(
                CASE WHEN
                        DESCRIPTION IN ('D1','D2','D3')
                    AND CONTENTS NOT IN ('N','',' ') THEN 1
                    ELSE NULL 
                END) > 0 THEN '*'
            WHEN COUNT(
                CASE WHEN
                        DESCRIPTION IN ('D1','D2','D3') THEN 1
                    ELSE NULL
                END) > 0 THEN 'X'
            ELSE '_'
        END
FROM TABLE
WHERE USERID = '123456789'

修改

要删除重复的DESCRIPTION IN ('D1'...)(来自第一个代码段):

SELECT  CASE
            WHEN COUNT_DESCRIPTION > 0 AND COUNT_CONTENTS > 0 THEN '*'
            WHEN COUNT_DESCRIPTION > 0 THEN 'X'
            ELSE '_'
        END
FROM (
    SELECT  USERID, 
        COUNT(CASE WHEN DESCRIPTION IN ('D1','D2','D3') THEN 1 ELSE NULL END) AS COUNT_DESCRIPTION,
        COUNT(CASE WHEN CONTENTS NOT IN ('N','',' ') THEN 1 ELSE NULL END) AS COUNT_CONTENTS
    FROM    TABLE
    GROUP BY USERID
) AS x
WHERE   USERID = '123456789'

但是,如果某些USERID的行包含DESCRIPTION IN ('D1','D2','D3')且没有CONTENTS NOT IN ('N','',' ')(即CONTENTS IN ('N','',' ')),其他,则无效行CONTENTS NOT IN ('N','',' ')且没有DESCRIPTION IN ('D1','D2','D3')的行。对于此类USERID,最后一个查询将返回' *',但实际上应该返回' X' (DESCRIPTION IN ('D1','D2','D3')的行集和CONTENTS NOT IN ('N','',' ')的行集是不相交的。)

编辑2 (基于上述内容的新提案,希望有效)

SELECT  CASE
            WHEN COUNT(DESCRIPTION_PREDICATE) > 0 AND COUNT(CONTENTS_PREDICATE) > 0 THEN '*'
            WHEN COUNT(CONTENTS_PREDICATE) > 0 THEN 'X'
            ELSE '_'
        END
FROM (
    SELECT  USERID, 
        CASE WHEN DESCRIPTION IN ('D1','D2','D3') THEN 1 ELSE NULL END AS DESCRIPTION_PREDICATE,
        CASE WHEN CONTENTS NOT IN ('N','',' ') THEN 1 ELSE NULL END) AS CONTENTS_PREDICATE
    FROM    TABLE
) AS x
WHERE   USERID = '123456789'
GROUP BY USERID

答案 1 :(得分:0)

基于rslemos之前发布的内容,我能够找到符合我要求的解决方案,并且能够轻松阅读和理解。

SELECT
    CASE
        WHEN COUNT(
            CASE
                WHEN CONTENTS NOT IN ('N','',' ') THEN 1
                ELSE NULL 
            END) > 0 THEN '*'
        WHEN COUNT(*) > 0 THEN 'X'
        ELSE '_'
    END AS 'RESULT'
FROM TABLE
WHERE USERID = '123456789'
AND DESCRIPTION IN ('D1','D2','D3')

由于我使用的是COUNT(*),所以我仍然对这个问题有点不确定,但到目前为止,我已经通过了我需要通过的测试。