开发者

Horizontal DISTINCT COUNT in SQL Server or a COUNT IF without counting duplicates

I have over simplified the examples that follow, however please know that I really do need the results as I describe.

I have a table that contains information about customers including how they were referred. For example MyTable (EDIT: My over simplification resulted in answers that would not work. They are good answers, but do not solve my problem. I have added the last piece of the puzzle for clarity):

STORE    CUS_ID REFERRED    MONEYMADE  
 1        11        RADIO         10.00
 1        15        WALKIN        20.00
 1        11        RADIO         30.00
 2        12        RADIO         40.00
 2        12        RADIO         50.00
 3        13        WALKIN        60.00
 3        14        WALKIN        70.00

I want to count the number of DISTINCT customers that were referred by a certain type and group them by store. I could do the following:

SELECT 
   STORE, COUNT(DISTINCT CUS_ID) AS COUNTEM, 
   REFERRED, SUM(MONEYMADE) 
FROM MyTable 
GROUP BY REFERRED, STORE

This gives the following result:

STORE    COUNTEM    REFERRED    MONEYMADE
 1        1         RADIO        40.00
 1        1         WALKIN       20.00
 2        1         RADIO        90.00
 3        2         WALKIN       130.00

However, I really need the results like this:

STORE    RADIO  RADIO_MONEY   WALKIN   WALKIN_MONEY
 1        1      40.00         1         20.00
 2        1      90.00         0         0.00
 3        0      0.00          2         130.00

I attempted a query using SUM and CASE but it counts duplicates. For 开发者_开发问答example:

SELECT 
   STORE, 
   SUM (CASE REFERRED WHEN 'RADIO' THEN 1 ELSE 0 END) AS RADIO,  
   SUM (CASE REFERRED WHEN 'RADIO' THEN MONEYMADE ELSE 0 END) AS RADIO_MONEY,  
   SUM (CASE REFERRED WHEN 'WALKIN' THEN 1 ELSE 0 END) AS WALKIN
   SUM (CASE REFERRED WHEN 'WALKIN' THEN MONEYMADE ELSE 0 END) AS WALKIN_MONEY, 
FROM MyTable
GROUP BY STORE

Returns this incorrect result:

STORE    RADIO  RADIO_MONEY   WALKIN   WALKIN_MONEY
 1        2      40.00         1         20.00
 2        2      90.00         0         0.00
 3        0      0.00          2         130.00

Is there any way to pull this off? I have searched for "count if" type of functions but the only thing I have found is the sum - case method.


EDIT Since you need to SUM another Field. you should do the sum before the SUM/CASE

With self-contained sample data

--TEST DATA
DECLARE @MyTable table 
(STORE int,  CUST_ID int , Referred varchar(6) ,MONEYMADE   Money )

INSERT INTO @MyTable VALUES  
(1,        11,        'RADIO' ,        10.00)
INSERT INTO @MyTable VALUES  
(1,        15,        'WALKIN',        20.00)
INSERT INTO @MyTable VALUES 
(1,        11,        'RADIO' ,       30.00)
INSERT INTO @MyTable VALUES 
(2,        12,        'RADIO' ,        40.00)
INSERT INTO @MyTable VALUES 
(2,        12,        'RADIO' ,       50.00)
INSERT INTO @MyTable VALUES 
(3,        13,        'WALKIN',        60.00)
INSERT INTO @MyTable VALUES 
(3,        14,        'WALKIN',        70.00)

--USING PIVOT

  SELECT 
    pk.STORE,
    pk.RADIO,
    pk.WALKIN,
    ISNULL(pv.RADIO,0) as RADIO_MONEY,
    ISNULL(pv.WALKIN,0)as WALKIN_MONEY

 FROM
 (SELECT  STORE,   
          REFERRED ,
          MONEYMADE   
  FROM  @MyTable) p
          PIVOT (SUM(MONEYMADE) FOR REFERRED in (WALKIN, RADIO)) as  pv
  INNER JOIN         
 ( SELECT  DISTINCT STORE,   
                    REFERRED ,
                    CUST_ID   
   FROM  @MyTable) p
            PIVOT ( COUNT (CUST_ID) FOR REFERRED in (WALKIN, RADIO)) as  pk
  ON pv.store = pk.STORE

--WITHOUT Using PIVOT

SELECT 
   STORE,
   SUM(CASE REFERRED WHEN  'RADIO' THEN 1 ELSE 0 END )AS RADIO,
   SUM(CASE REFERRED WHEN 'WALKIN' THEN 1 ELSE 0 END )AS WALKIN,
   SUM(CASE REFERRED WHEN  'RADIO' THEN MONEYMADE ELSE 0 END )AS RADIO_MONEYMADE,
   SUM(CASE REFERRED WHEN 'WALKIN' THEN MONEYMADE ELSE 0 END )AS WALKIN_MONEYMADE
FROM   
   (

SELECT  
    STORE,   
    CUST_ID, 
    REFERRED ,
    SUM(MONEYMADE )MONEYMADE
  FROM 
    @MyTable p
  GROUP BY 
  STORE,
  REFERRED,
  CUST_ID


) t
group by store

Output For both techniques

    STORE       RADIO       WALKIN      RADIO_MONEY           WALKIN_MONEY
    ----------- ----------- ----------- --------------------- ---------------------
    1           1           1           40.00                 20.00
    2           1           0           90.00                 0.00
    3           0           2           0.00                  130.00


Rotating SQL results is at minimum tricky. If you want to present data right from the query, you may have to sacrifice query flexibility and hardcode a good deal of information (like column names and their count).

So what I'd like to suggest is doing a bit more on the backend, where the query is actually used. If it's acceptable and you can rotate the results yourself then you can the following:

;with R(Referred) as (
  select distinct REFERRED from MyTable
)
select t.STORE, COUNT(*) as COUNT, t.REFERRED
from MyTable t
  left join R on r.REFERRED = t.REFERRED
group by t.STORE, t.REFERRED


Here's your script modified to comply, I think, with what you are trying to achieve:

SELECT 
   STORE, 
   COUNT(DISTINCT CASE REFERRED WHEN 'RADIO' THEN CUS_ID END) AS RADIO,  
   SUM  (CASE REFERRED WHEN 'RADIO' THEN MONEYMADE ELSE 0 END) AS RADIO_MONEY,  
   COUNT(DISTINCT CASE REFERRED WHEN 'WALKIN' THEN CUS_ID END) AS WALKIN
   SUM  (CASE REFERRED WHEN 'WALKIN' THEN MONEYMADE ELSE 0 END) AS WALKIN_MONEY, 
FROM MyTable
GROUP BY STORE
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜