Pivot table using SQL Server 2008 R2 - sql

I am getting count issue in the pivot table query using the SQL Server 2008 R2.
Example:
I have a table testrf:
CREATE TABLE testrf
(
cola INTEGER,
colb VARCHAR(10)
)
With some data:
INSERT INTO testrf VALUES(1,'x')
INSERT INTO testrf VALUES(1,'x')
INSERT INTO testrf VALUES(2,'x')
INSERT INTO testrf VALUES(3,'y')
INSERT INTO testrf VALUES(4,'y')
INSERT INTO testrf VALUES(5,'c')
INSERT INTO testrf VALUES(6,'c')
INSERT INTO testrf VALUES(7,'c')
INSERT INTO testrf VALUES(8,'d')
INSERT INTO testrf VALUES(3,'y')
INSERT INTO testrf VALUES(12,'M1')
INSERT INTO testrf VALUES(13,'L1')
INSERT INTO testrf VALUES(14,'C2')
Looks like this:
SELECT * FROM testrf;
cola colb
------------
1 x
1 x
2 x
3 y
4 y
5 c
6 c
7 c
8 d
3 y
12 M1
13 L1
14 C2
My Pivot table query:
DECLARE #cols NVARCHAR(MAX)
DECLARE #SQL NVARCHAR(MAX)
SELECT #cols = STUFF ( (SELECT DISTINCT '],[' + v.colb
FROM testrf AS v
FOR XML PATH('')), 1, 2, '') + ']'
SET #SQL = N'SELECT cola,'+ #cols +'
FROM
(SELECT v.cola,v.colb
FROM testrf AS v
GROUP BY v.cola,v.colb
) p
PIVOT
(
count(colb)
FOR colb IN ( '+ #cols + ' )
) AS pvt';
EXEC(#SQL)
Getting Result:
cola c C2 d L1 M1 x y
----------------------------------
1 0 0 0 0 0 1 0
2 0 0 0 0 0 1 0
3 0 0 0 0 0 0 1
4 0 0 0 0 0 0 1
5 1 0 0 0 0 0 0
6 1 0 0 0 0 0 0
7 1 0 0 0 0 0 0
8 0 0 1 0 0 0 0
12 0 0 0 0 1 0 0
13 0 0 0 1 0 0 0
14 0 1 0 0 0 0 0
Note: colb=x is occurred 2 times for cola=1and colb=y occurred 2 times for cola=3. Getting counting issue here.

You don't require the GROUP BY clause within the inner subquery (p)
DECLARE #cols NVARCHAR(MAX)
DECLARE #SQL NVARCHAR(MAX)
SELECT #cols = STUFF ( (SELECT DISTINCT ',' + QUOTENAME(v.colb)
FROM testrf AS v
FOR XML PATH('')), 1, 1, '')
SET #SQL = N'SELECT cola,'+ #cols +'
FROM
(SELECT v.cola,v.colb
FROM testrf AS v
) p
PIVOT
(
count(colb)
FOR colb IN ( '+ #cols + ' )
) AS pvt'
EXEC(#SQL)
;
I have also proposed a change to your #cols by using QUOTENAME()
From your sample data this produces the following result:
| COLA | C | C2 | D | L1 | M1 | X | Y |
|------|---|----|---|----|----|---|---|
| 1 | 0 | 0 | 0 | 0 | 0 | 2 | 0 |
| 2 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
| 3 | 0 | 0 | 0 | 0 | 0 | 0 | 2 |
| 4 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 5 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 6 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 8 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| 12 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| 13 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| 14 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
See this at SQLfiddle

Related

How to combine data from 2 tables -- which join, what conditions?

Consider the following 2 tables.
TableDE
ID country key1 key2
------------------------
1 US 1 null
1 US 1 null
1 US 1 null
2 US null null
3 US 1 1
4 DE 1 1
5 DE null null
5 DE null null
TableUS
ID key1 key2
--------------
1 null null
2 null 1
4 1 1
8 null 1
2 null 1
2 null 1
9 1 null
I need a distinct overview of all IDs, combining data from both tables:
ID inTableDe country DEkey1 DEkey2 inTableUS USkey1 USKey2
-----------------------------------------------------------------
1 1 US 1 0 1 0 0
2 1 US 0 0 1 0 1
3 1 US 1 1 0 0 0
4 1 DE 1 1 1 1 1
5 1 DE 0 0 0 0 0
8 0 0 0 1 1 0 1
9 0 0 0 1 1 1 0
I hope it speaks for itself:
ID 8 and ID 9 have 0 in the first column bc they aren't in tableDE
ID 8 and ID 9 have 0 in the country column bc this field doesn't exist in tableUS
ID 3 has 0 in inTableUS bc it only exists in tableDE
the key values are copied from the original tables
an ID is not unique: it can appear many times in both tables. However: the values for key1 and key2 will always be the same for each ID within the same table.
I have been messing for hours now with this; I have this now:
select de.[ID],
de.[country],
case when (de.[ID] in (select distinct [ID] from [tableDE]) then 1 else 0 end as [inTableDE],
case when (de.[ID] in (select distinct [ID] from [tableUS]) then 1 else 0 end as [inTableUS],
de.[key1] as [DEKey1],
de.[key2] as [DEKey2],
us.[key1] as [USKey1],
us.[key2] as [USKey2],
from dbo.[tableDE] de
full outer join dbo.[tableUS] us on de.[ID] = us.[ID]
where de.[country] = 'US'
and (de.[key1] = 1 or de.[key2] = 1 or us.[key1] = 1 or us.[key2] = 1)
group by de.[ID], us.[ID]
But this keeps giving me only values that are in both tables.
What am I doing wrong?
You sem to want aggregation on top of the full join:
select
coalesce(de.id, us.id) as id,
case when de.id is null then 0 else 1 end as intablede,
max(de.country) as country,
coalesce(max(de.key1), 0) as dekey1,
coalesce(max(de.key2), 0) as dekey2,
case when us.id is null then 0 else 1 end as intableus,
coalesce(max(us.key1), 0) as uskey1,
coalesce(max(us.key2), 0) as uskey2
from dbo.tablede de
full join dbo.tableus us on de.id = us.id
group by de.id, us.id
order by id
Demo on DB Fiddle:
id | intablede | country | dekey1 | dekey2 | intableus | uskey1 | uskey2
-: | --------: | :------ | -----: | -----: | --------: | -----: | -----:
1 | 1 | US | 1 | 0 | 1 | 0 | 0
2 | 1 | US | 0 | 0 | 1 | 0 | 1
3 | 1 | US | 1 | 1 | 0 | 0 | 0
4 | 1 | DE | 1 | 1 | 1 | 1 | 1
5 | 1 | DE | 0 | 0 | 0 | 0 | 0
8 | 0 | null | 0 | 0 | 1 | 0 | 1
9 | 0 | null | 0 | 0 | 1 | 1 | 0

SQL Server - Sum based on another column, and combine with STUFF/XML PATH?

I'm not sure if this is incredibly convoluted, so apologies in advance.
CREATE TABLE TestOrder
(
[OrderLineID] INT NOT NULL IDENTITY PRIMARY KEY,
[Item] NVARCHAR(30) NULL,
[OrderQty] INT NULL,
[Status1] INT NULL,
[Quantity_at_sts1] INT NULL,
[Status2] INT NULL,
[Quantity_at_sts2] INT NULL,
[Status3] INT NULL,
[Quantity_at_sts3] INT NULL,
[Status4] INT NULL,
[Quantity_at_sts4] INT NULL,
[Status5] INT NULL,
[Quantity_at_sts5] INT NULL,
[OrderRef] NVARCHAR(10) NULL
)
INSERT INTO TestOrder
values
('TSHIRT','1','100','1','0','0','0','0','0','0','0','0','Ord.1'),
('SOCKS','4','50','4','0','0','0','0','0','0','0','0','Ord.2'),
('SHIRT','5','10','1','50','2','0','0','0','0','0','0','Ord.3'),
('SHIRT','5','100','2','0','0','0','0','0','0','0','0','Ord.3'),
('SOCKS','10','10','4','50','2','0','0','0','0','0','0','Ord.4'),
('SOCKS','10','50','3','100','1','0','0','0','0','0','0','Ord.4')
.
| OrderLineID | Item | OrderQty | Status1 | Quantity_at_sts1 | Status2 | Quantity_at_sts2 | Status3 | Quantity_at_sts3 | Status4 | Quantity_at_sts_4 | Status5 | Quantity_at_sts5 | OrderRef |
|-------------|------|----------|---------|------------------|---------|------------------|---------|------------------|---------|-------------------|---------|------------------|----------|
| 1 |TSHIRT| 1 | 100 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Ord.1 |
| 2 |SOCKS | 4 | 50 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Ord.2 |
| 3 |SHIRT | 5 | 10 | 1 | 50 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | Ord.3 |
| 4 |SHIRT | 5 | 100 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Ord.3 |
| 5 |SOCKS | 10 | 10 | 4 | 50 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | Ord.4 |
| 6 |SOCKS | 10 | 50 | 3 | 100 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | Ord.4 |
So this table is effectively an order table, and all of the "status1, quantity_at_sts1, status2..." etc. columns are effectively the progress of that order. Imagine you have various statuses between 10 to 100, 10 being 'not started', 100 being 'complete and despatched'. The "OrderRef" column is actually the key identifier for these orders of interest; e.g. the 4 rows for 'Ord.3' and 'Ord.4' are actually just one single item on each order, but with different statuses on each row.
Up till now, I've been using a case statement to get all of these statuses into one easier-to-read column (ignore all the casts as they are only required on the actual data):
select
CASE
WHEN status2 = 0 THEN '['+CAST(status1 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts1 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10))
WHEN status2 > 0 AND status3 = 0 THEN '['+CAST(status1 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts1 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10))+' - '+'['+CAST(status2 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts2 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10))
WHEN status3 > 0 AND status4 = 0 THEN '['+CAST(status1 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts1 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10))+' - '+'['+CAST(status2 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts2 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10))+' - ['+CAST(status3 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts3 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10))
WHEN status4 > 0 AND status5 = 0 THEN '['+CAST(status1 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts1 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10))+' - '+'['+CAST(status2 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts2 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10))+' - ['+CAST(status3 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts3 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10)) + ' - ['+CAST(status4 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts4 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10))
when status5 > 0 then '['+CAST(status1 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts1 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10))+' - '+'['+CAST(status2 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts2 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10))+' - ['+CAST(status3 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts3 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10)) + ' - ['+CAST(status4 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts4 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10)) + ' - ['+CAST(status5 AS NVARCHAR(10))+'] '+CAST(CAST(CAST(quantity_at_sts5 AS DECIMAL(20, 6)) AS FLOAT) AS NVARCHAR(10))
END AS 'all_statuses'
, *
from testorder
.
| all_statuses | OrderLineID | Item | OrderQty | Status1 | Quantity_at_sts1 | Status2 | Quantity_at_sts2 | Status3 | Quantity_at_sts3 | Status4 | Quantity_at_sts_4 | Status5 | Quantity_at_sts5 | OrderRef |
|------------------|--------------|------|----------|---------|------------------|---------|------------------|---------|------------------|---------|-------------------|---------|------------------|----------|
| [100] 1 | 1 |TSHIRT| 1 | 100 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Ord.1 |
| [50] 4 | 2 |SOCKS | 4 | 50 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Ord.2 |
| [10] 1 - [50] 2 | 3 |SHIRT | 5 | 10 | 1 | 50 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | Ord.3 |
| [100] 2 | 4 |SHIRT | 5 | 100 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Ord.3 |
| [10] 4 - [50] 2 | 5 |SOCKS | 10 | 10 | 4 | 50 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | Ord.4 |
| [50] 3 - [100] 1 | 6 |SOCKS | 10 | 50 | 3 | 100 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | Ord.4 |
My ideal is to roll up by each unique OrderRef value, with the all_statuses being sum quantities across ALL rows with that OrderRef value:
| all_statuses | OrderQty | OrderRef |
|----------------------------|------------|-----------|
| [100] 1 | 1 | Ord.1 |
| [50] 4 | 4 | Ord.2 |
| [10] 1 - [50] 2 - [100] 2 | 5 | Ord.3 |
| [10] 4 - [50] 5 - [100] 1 | 10 | Ord.4 |
, however I have 2 issues I am struggling with:
Getting all of the statuses (even across multiple rows) into a single column
Summing up the quantities across multiple rows that have the SAME status (e.g. in Ord.4's case)
I have a little experience with STUFF FOR XML PATH and thought I might be able to do some kind of ridiculous CASE with STUFF combination, but I'm struggling to find a method. Is this a little too complicated to do?
The first step is to unpivot your columns to rows, which you can do using CROSS APPLY and a table value constructor
SELECT o.OrderRef, x.*
FROM #TestOrder AS o
CROSS APPLY
(VALUES
(1, o.Status1, o.Quantity_at_sts1),
(2, o.Status2, o.Quantity_at_sts2),
(3, o.Status3, o.Quantity_at_sts3),
(4, o.Status4, o.Quantity_at_sts4),
(5, o.Status5, o.Quantity_at_sts5)
) x (StatusNumber, StatusID, Quantity)
WHERE x.StatusID > 0;
Which gives:
OrderRef StatusNumber StatusID Quantity
--------------------------------------------------
Ord.1 1 100 1
Ord.2 1 50 4
Ord.3 1 10 1
Ord.3 2 50 2
Ord.3 1 100 2
Ord.4 1 10 4
Ord.4 2 50 2
Ord.4 1 50 3
Ord.4 2 100 1
Then you need to sum up the quantity, grouped by order, which is a simple SUM/GROUP BY. I have also added a ROW_NUMBER() column to use later when ordering your items during concatenation:
SELECT o.OrderRef,
Quantity = SUM(x.Quantity),
x.StatusID,
RowNumber = ROW_NUMBER() OVER(PARTITION BY o.OrderRef ORDER BY MIN(o.OrderLineID), MIN(x.StatusNumber))
FROM #TestOrder AS o
CROSS APPLY
(VALUES
(1, o.Status1, o.Quantity_at_sts1),
(2, o.Status2, o.Quantity_at_sts2),
(3, o.Status3, o.Quantity_at_sts3),
(4, o.Status4, o.Quantity_at_sts4),
(5, o.Status5, o.Quantity_at_sts5)
) x (StatusNumber, StatusID, Quantity)
WHERE x.StatusID > 0
GROUP BY x.StatusID, o.OrderRef
OrderRef Quantity StatusID RowNumber
----------------------------------------------
Ord.1 1 100 1
Ord.2 4 50 1
Ord.3 1 10 1
Ord.3 2 50 2
Ord.3 2 100 3
Ord.4 4 10 1
Ord.4 5 50 2 <-- TWO ROWS GROUPED HERE
Ord.4 1 100 3
Finally, you can use the above derived table to create your strings, and then concatenate them using SQL Server's XML extensions:
WITH ALLStatuses AS
( SELECT o.OrderRef,
Quantity = SUM(x.Quantity),
x.StatusID,
RowNumber = ROW_NUMBER() OVER(PARTITION BY o.OrderRef ORDER BY MIN(o.OrderLineID), MIN(x.StatusNumber))
FROM #TestOrder AS o
CROSS APPLY
(VALUES
(1, o.Status1, o.Quantity_at_sts1),
(2, o.Status2, o.Quantity_at_sts2),
(3, o.Status3, o.Quantity_at_sts3),
(4, o.Status4, o.Quantity_at_sts4),
(5, o.Status5, o.Quantity_at_sts5)
) x (StatusNumber, StatusID, Quantity)
WHERE x.StatusID > 0
GROUP BY x.StatusID, o.OrderRef
)
SELECT all_statuses = STUFF(( SELECT CONCAT(' - ', QUOTENAME(S.StatusID), ' ', s.Quantity)
FROM ALLStatuses AS s
WHERE s.OrderRef = o.OrderRef
ORDER BY RowNumber
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 3, ''),
o.OrderQty,
o.OrderRef
FROM #TestOrder AS o
GROUP BY o.OrderRef, o.OrderQty;
Output
all_statuses OrderQty OrderRef
------------------------------------------------
[100] 1 1 Ord.1
[50] 4 4 Ord.2
[10] 1 - [50] 2 - [100] 2 5 Ord.3
[10] 4 - [50] 5 - [100] 1 10 Ord.4

order by a string as number sql

I have a table like follows, i need to pivot the table with weeks. i could able to create pivot table but the columns order getting shuffled since im ordering a string column. and getting output like 3nd table.
MRN Weeks
--------------------------------
GIRFTR1H0461081 52
GIRFTR1H0461083 5 - 6
GIRFTR1H0461084 0 - 1
GIRFTR1H0461085 1 - 2
GIRFTR1H0461086 11 - 12
GIRFTR1H0461087 1 - 2
I need to get output as a pivot table order by Weeks Like,
MRN | 0 - 1 | 1 - 2 | 5 - 6 |11 - 12| 52
---------------------------------------------------------------
GIRFTR1H0461081 | 0 | 0 | 0 | 0 | 0
GIRFTR1H0461083 | 0 | 0 | 1 | 0 | 0
GIRFTR1H0461084 | 1 | 0 | 0 | 0 | 0
GIRFTR1H0461085 | 0 | 1 | 0 | 0 | 0
GIRFTR1H0461086 | 0 | 0 | 0 | 1 | 0
GIRFTR1H0461087 0 | 1 | 0 | 0 | 0
When i tried to create this table, the columns order getting shuffled like bellow,
MRN | 0 - 1 | 1 - 2 |11 - 12| 5 - 6 | 52
---------------------------------------------------------------
GIRFTR1H0461081 | 0 | 0 | 0 | 0 | 0
GIRFTR1H0461083 | 0 | 0 | 0 | 1 | 0
GIRFTR1H0461084 | 1 | 0 | 0 | 0 | 0
GIRFTR1H0461085 | 0 | 1 | 0 | 0 | 0
GIRFTR1H0461086 | 0 | 0 | 1 | 0 | 0
GIRFTR1H0461087 | 0 | 1 | 0 | 0 | 0
My code is,
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(WeeksBand)
from [test_Tbl]
group by WeeksBand
order by WeeksBand
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT CurrentSpecialty,' + #cols + ' from
(
select CurrentSpecialty, WeeksBand, EncounterId
from [Tbl_Current_PTL_Test]
) x
pivot
(
count(EncounterId)
for WeeksBand in (' + #cols + ')
) p '
execute(#query);
Please help me to sort out my issue,
It's doing a string (alpha) sort, and you actually want to be doing a numeric sort.
I'd change the order line from:
order by WeeksBand
to:
order by Cast(SubString(WeeksBand, 1, CASE WHEN CHARINDEX('-', WeeksBand) != 0 THEN CHARINDEX('-', WeeksBand) -1 ELSE LEN(WeeksBand) END) AS INT)
which will convert the first number into an integer value (not string) and then should order correctly.
I don't see why a standard pivot query should not work here. From your expected output it seems you want a 1 as a placeholder for an MRN entry which is present in that week, and 0 otherwise.
SELECT [MRN],
MAX(CASE WHEN [Weeks] = '0 - 1' THEN 1 ELSE 0 END) AS [0 - 1],
MAX(CASE WHEN [Weeks] = '1 - 2' THEN 1 ELSE 0 END) AS [1 - 2],
MAX(CASE WHEN [Weeks] = '5 - 6' THEN 1 ELSE 0 END) AS [5 - 6],
MAX(CASE WHEN [Weeks] = '11 - 12' THEN 1 ELSE 0 END) AS [11 - 12],
MAX(CASE WHEN [Weeks] = '52' THEN 1 ELSE 0 END) AS [52]
FROM [yourTable]
GROUP BY [MRN]
You should format Week field of first table using 2 digits as:
MRN Weeks
--------------------------------
GIRFTR1H0461081 52
GIRFTR1H0461083 05 - 06
GIRFTR1H0461084 00 - 01
GIRFTR1H0461085 01 - 02
GIRFTR1H0461086 11 - 12
GIRFTR1H0461087 01 - 02

Need a select query to get the output as shown below.?

I Have a SQL Table as shown below,
| Loc | Date | Id | Sts |
-------------------------
| Hyd | 15-01-2016 | 1 | A |
| Vjd | 16-01-2016 | 2 | B |
| Viz | 15-01-2016 | 3 | C |
| Hyd | 15-03-2016 | 4 | A |
| Vjd | 15-03-2016 | 5 | B |
| Viz | 15-03-2016 | 6 | C |
| Hyd | 15-03-2016 | 4 | A |
| Vjd | 15-05-2016 | 5 | B |
| Viz | 15-05-2016 | 6 | C |
And i need output like,
**| Loc | Jan-16 | Mar-16 | May-16 |**
**|-------|A |B |C |A |B |C |A |B |C |**
----------
|Hyd | 1 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 |
|Vjd | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 |
|Viz | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
Can anyone help me out please..
Thanks in Advance.
You will basically need to aggregate based on CASE statements, like this:
DECLARE #table TABLE (loc VARCHAR(3), [date] DATE, id INT, sts CHAR(1));
INSERT INTO #table SELECT 'Hyd', '20160115', 1, 'A';
INSERT INTO #table SELECT 'Vjd', '20160116', 2, 'B';
INSERT INTO #table SELECT 'Viz', '20160115', 3, 'C';
INSERT INTO #table SELECT 'Hyd', '20160315', 4, 'A';
INSERT INTO #table SELECT 'Vjd', '20160315', 5, 'B';
INSERT INTO #table SELECT 'Viz', '20160315', 6, 'C';
INSERT INTO #table SELECT 'Hyd', '20160315', 4, 'A';
INSERT INTO #table SELECT 'Vjd', '20160515', 5, 'B';
INSERT INTO #table SELECT 'Viz', '20160515', 6, 'C';
SELECT
loc,
COUNT(CASE WHEN YEAR([date]) = 2016 AND MONTH([date]) = 1 AND sts = 'A' THEN 1 END) AS Jan_A,
COUNT(CASE WHEN YEAR([date]) = 2016 AND MONTH([date]) = 1 AND sts = 'B' THEN 1 END) AS Jan_B,
COUNT(CASE WHEN YEAR([date]) = 2016 AND MONTH([date]) = 1 AND sts = 'C' THEN 1 END) AS Jan_C,
COUNT(CASE WHEN YEAR([date]) = 2016 AND MONTH([date]) = 3 AND sts = 'A' THEN 1 END) AS Mar_A,
COUNT(CASE WHEN YEAR([date]) = 2016 AND MONTH([date]) = 3 AND sts = 'B' THEN 1 END) AS Mar_B,
COUNT(CASE WHEN YEAR([date]) = 2016 AND MONTH([date]) = 3 AND sts = 'C' THEN 1 END) AS Mar_C,
COUNT(CASE WHEN YEAR([date]) = 2016 AND MONTH([date]) = 5 AND sts = 'A' THEN 1 END) AS May_A,
COUNT(CASE WHEN YEAR([date]) = 2016 AND MONTH([date]) = 5 AND sts = 'B' THEN 1 END) AS May_B,
COUNT(CASE WHEN YEAR([date]) = 2016 AND MONTH([date]) = 5 AND sts = 'C' THEN 1 END) AS May_C
FROM
#table
GROUP BY
loc;
Results:
loc Jan_A Jan_B Jan_C Mar_A Mar_B Mar_C May_A May_B May_C
Hyd 1 0 0 2 0 0 0 0 0
Viz 0 0 1 0 0 1 0 0 1
Vjd 0 1 0 0 1 0 0 1 0

SQL Query to COUNT fields that match a certain value across all rows in a table

I am trying (and failing) to craft a simple SQL query (for SQL Server 2012) that counts the number of occurrences of a value for a given date range.
This is a collection of results from a survey.
So the end result would show there are only 3 lots of values matching '2' and
6 values matching '1'.
Even better if the final result could return 3 values:
MatchZero = 62
MatchOne = 6
MatchTwo = 3
Something Like (I know this is horribly out):
SELECT
COUNT(0) AS MatchZero,
COUNT(1) AS MatchOne,
COUNT(2) As MatchTwo
WHERE dated BETWEEN '2014-01-01' AND '2014-02-01'
I don't need it grouped by date or anything, simply a total value for each.
Any insights would be greatly received.
+------------+----------+--------------+-------------+------+-----------+------------+
| QuestionId | friendly | professional | comfortable | rate | recommend | dated |
+------------+----------+--------------+-------------+------+-----------+------------+
| 3 | 0 | 0 | 0 | 0 | 0 | 2014-02-12 |
| 9 | 0 | 0 | 0 | 0 | 0 | 2014-02-12 |
| 14 | 0 | 0 | 0 | 2 | 0 | 2014-02-13 |
| 15 | 0 | 0 | 0 | 0 | 0 | 2014-01-06 |
| 19 | 0 | 1 | 2 | 0 | 0 | 2014-01-01 |
| 20 | 0 | 0 | 0 | 0 | 0 | 2013-12-01 |
| 21 | 0 | 1 | 0 | 0 | 0 | 2014-01-01 |
| 22 | 0 | 1 | 0 | 0 | 0 | 2014-01-01 |
| 23 | 0 | 0 | 0 | 0 | 0 | 2014-01-24 |
| 27 | 0 | 0 | 0 | 0 | 0 | 2014-01-31 |
| 30 | 0 | 1 | 2 | 0 | 0 | 2014-01-27 |
| 31 | 0 | 0 | 0 | 0 | 0 | 2014-01-11 |
| 36 | 0 | 0 | 0 | 1 | 1 | 2014-01-22 |
+------------+----------+--------------+-------------+------+-----------+------------+
You can use conditional aggregation:
SELECT SUM((CASE WHEN friendly = 0 THEN 1 ELSE 0 END) +
(CASE WHEN professional = 0 THEN 1 ELSE 0 END) +
(CASE WHEN comfortable = 0 THEN 1 ELSE 0 END) +
(CASE WHEN rate = 0 THEN 1 ELSE 0 END) +
(CASE WHEN recommend = 0 THEN 1 ELSE 0 END) +
) AS MatchZero,
SUM((CASE WHEN friendly = 1 THEN 1 ELSE 0 END) +
(CASE WHEN professional = 1 THEN 1 ELSE 0 END) +
(CASE WHEN comfortable = 1 THEN 1 ELSE 0 END) +
(CASE WHEN rate = 1 THEN 1 ELSE 0 END) +
(CASE WHEN recommend = 1 THEN 1 ELSE 0 END) +
) AS MatchOne,
SUM((CASE WHEN friendly = 2 THEN 1 ELSE 0 END) +
(CASE WHEN professional = 2 THEN 1 ELSE 0 END) +
(CASE WHEN comfortable = 2 THEN 1 ELSE 0 END) +
(CASE WHEN rate = 2 THEN 1 ELSE 0 END) +
(CASE WHEN recommend = 2 THEN 1 ELSE 0 END) +
) AS MatchTwo
FROM . . .
WHERE dated BETWEEN '2014-01-01' AND '2014-02-01';
If I understand you correctly, you want to count the zeros, ones and twos for a particular (or each) column in your table. If this is correct, then you could do something like this:
select sum(case when your_column = 0 then 1 else 0 end) as zeros
, sum(case when your_column = 1 then 1 else 0 end) as ones
--- and so on
from your_table
-- where conditions go here
If you want to count the total for more than one column, enclose the needed case...ends in the sum():
sum(
(case when column1 = 0 then 1 else 0 end) +
(case when column2 = 0 then 1 else 0 end)
-- and so on
) as zeros
Going with a simple unpivot you can get the desired result with less coding.
By simply changing the date range the correct count of each question type is counted.
SELECT
RANKING, COUNT(*) AS CNT
FROM
(SELECT
friendly,professional,comfortable,rate,recommend
FROM
your_table
WHERE
dated >= '1/1/1900' AND dated <= '1/1/2015'
) AS U UNPIVOT
(RANKING FOR QUESTION IN (friendly,professional,comfortable,rate,recommend)) AS UNP
GROUP BY
RANKING