COUNT(DISTINCT) in multiple columns in SQL Server 2008

南楼画角 提交于 2020-01-24 05:54:59

问题


In Oracle, it's possible to get a count of distinct values in multiple columns by using the || operator (according to this forum post, anyway):

SELECT COUNT(DISTINCT ColumnA || ColumnB) FROM MyTable

Is there a way to do this in SQL Server 2008? I'm trying to perform a single query to return some group statistics, but I can't seem to do it.

For example, here is a table of values I'm trying to query:

AssetId MyId    TheirId   InStock
328     10      10        1
328     20      20        0
328     30      30        0
328     40      10        0
328     10      10        0
328     10      10        0
328     10      10        0
328     10      10        0

For AssetId #328, I want to compute the total number of unique IDs in the MyId and TheirId columns (4 = 10, 20, 30, 40), as well as the total number of non-zero rows in the InStock column (1):

AssetId     TotalIds    AvailableIds
328         4           1

Is there a way to work this magic somehow?


回答1:


You can use a cross apply and values.

select T1.AssetId,
       count(distinct T2.ID) TotalIds,
       sum(case T2.InStock when 0 then 0 else 1 end) AvailableIds 
from YourTable as T1
  cross apply(values(T1.MyId, T1.InStock),
                    (T1.TheirId, 0)
             ) as T2(ID, InStock)
group by T1.AssetId  

SE-Data

Or you can do a union all in a sub query.

select T.AssetId,
       count(distinct T.ID) TotalIds,
       sum(case T.InStock when 0 then 0 else 1 end) AvailableIds 
from (
     select AssetId, MyId as ID, InStock
     from YourTable
     union all
     select AssetID, TheirId, 0
     from YourTable
     ) as T
group by T.AssetId  



回答2:


I think it's good solution for you

SELECT COUNT(*)
FROM (SELECT DISTINCT Column1, Column2 
      FROM MyTable) A



回答3:


You can get the result like this:

DECLARE @t TABLE (AssetId INT, MyId    INT, TheirId   INT, InStock INT)
INSERT @t
VALUES 
(328,10, 10,   1)
,(328,20, 20,   0)
,(328,30, 30,   0)
,(328,40, 10,   0)
,(328,10, 10,   0)
,(328,10, 10,   0)
,(328,10, 10,   0)
,(328,10, 10,   0)

;WITH a AS(
    SELECT  AssetId,
            COUNT(col) cnt
    FROM 
    (
            SELECT  MyId col, AssetId
            FROM    @t
            UNION
            SELECT  TheirId col, AssetId
            FROM    @t
    ) b
    GROUP BY AssetId
)

SELECT  a.AssetId,
        a.cnt TotalIds,
        SUM(CASE WHEN InStock <> 0 THEN 1 ELSE 0 END) AvailableIds
FROM    @t c
JOIN    a ON a.AssetId = c.AssetId
GROUP   BY a.AssetId, a.cnt

In Common Table Expression (WITH code block) 'uniqueness' is guaranteed by using UNION operator which discards duplicate values, that's why COUNT(col) doesn't need to be used like COUNT(DISTINCT col).




回答4:


You can follow the Oracle example and concatenate the values together (that is what the Oracle query is doing). You just have to convert the values to characters first:

select AssetId,
       count(distinct cast(MyId as varchar(8000))+','+cast(TheirId as varchar(8000)
            ) totalIds,
       count(distinct case when inStock> 0
                           then cast(MyId as varchar(8000))+','+cast(TheirId as varchar(8000)
             end) as AvailableIds
from t
group by Assetid

You can also do it as a subquery:

select AssetId, count(*) as TotalIds,
       sum(case when inStock > 0 then 1 else 0 end) as AvailableIds
from (select AssetId, myId, theirId, max(inStock) as inStock
      from t
      group by AssetId, myId, theirId
     ) a
group by AssetId

"Theoretically", I like the second approach better, since it is more set-based. However, if you find yourself trying to count distinct combinations columns in several different variables, the string concatenation approach is more practical.




回答5:


If you don't like to use CTE's, you can try using following solution. The gist of it is to

  • select the TotalID's for each AssetID in a seperate subquery
  • select the AvailableIDs for each AssetID in a seperate subquery
  • JOIN the results of both subqueries to produce the final results.

The statement as is works on the entire table. You can get the results for a single AssetID by adding an appropriate where clause to the entire group.

SQL Statement

SELECT  a.AssetId, t.TotalIDs, a.AvailableIDs
FROM    (
            SELECT  AssetID, TotalIDs = COUNT(*) 
            FROM    (
                        SELECT  AssetID 
                        FROM    MyTable 
                        GROUP BY 
                                MyId, TheirID, AssetID
                    ) t 
            GROUP BY 
                    AssetID
        ) AS t
        INNER JOIN (
            SELECT  AssetID, AvailableIDs = SUM(InStock) 
            FROM    MyTable 
            GROUP BY 
                    AssetID
        ) AS a ON a.AssetId = t.AssetId

Test script

;WITH MyTable (AssetId, MyId, TheirId, InStock) AS (
    SELECT * FROM (VALUES 
        (328, 10, 10, 1)
        , (328, 20, 20, 0)
        , (328, 30, 30, 0)
        , (328, 40, 10, 0)
        , (328, 10, 10, 0)
        , (328, 10, 10, 0)
        , (328, 10, 10, 0)
        , (328, 10, 10, 0)
        , (329, 10, 10, 0)
        , (329, 10, 20, 1)
    ) AS a (b, c, d, e)
)
SELECT  a.AssetId, t.TotalIDs, a.AvailableIDs
FROM    (
            SELECT  AssetID, TotalIDs = COUNT(*) 
            FROM    (
                        SELECT  AssetID 
                        FROM    MyTable 
                        GROUP BY 
                                MyId, TheirID, AssetID
                    ) t 
            GROUP BY 
                    AssetID
        ) AS t
        INNER JOIN (
            SELECT  AssetID, AvailableIDs = SUM(InStock) 
            FROM    MyTable 
            GROUP BY 
                    AssetID
        ) AS a ON a.AssetId = t.AssetId



回答6:


Option #1

select 
          AssetID, count(distinct MyId) As MyId, SUM(InStock) InStock
From T
Group By 
      AssetID

Option #2 Without CTE

Select AssetID, count(MyId), sum(InStock) InStock From
(
    select 
            AssetID, MyId, SUM(InStock) InStock
    From MyTable
    Group By 
            AssetID, MyId
)K
Group by AssetID

Option #3 With CTE

;With Sub(AssetID, MyId, InStock)
As
(
    select 
            AssetID, MyId, SUM(InStock) InStock
    From MyTable
    Group By 
            AssetID, MyId
)

Select AssetID, count(MyId), sum(InStock) From
(
    Select * from Sub
)K


来源:https://stackoverflow.com/questions/11920317/countdistinct-in-multiple-columns-in-sql-server-2008

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!