Multiple pivots - sql-server

I have a table that looks like the following:
+---------+----------+---------+-------+-----------+-----------+-------------+
| ValueId | ObjectId | Field | Value | Estimated | OrigValue | FromDefault |
+---------+----------+---------+-------+-----------+-----------+-------------+
| 1 | 1 | 'Stat1' | 35 | true | (null) | (null) |
| 2 | 1 | 'Stat2' | 2 | false | 0 | true |
| 3 | 1 | 'Stat3' | 0.213 | true | 0.212 | false |
| 4 | 2 | 'Stat1' | 513 | true | 122 | true |
| 5 | 2 | 'Stat2' | 31 | true | (null) | true |
| 6 | 2 | 'Stat3' | 2.411 | true | (null) | false |
+---------+----------+---------+-------+-----------+-----------+-------------+
Fiddle: http://www.sqlfiddle.com/#!9/445271/2/0
And I want the pivot(s) to look like this:
+----------+-------+-------+-------+-----------------+-----------------+-------------------+-----------------+-----------------+-------------------+-----------------+-----------------+-------------------+
| ObjectId | Stat1 | Stat2 | Stat3 | Stat1_Estimated | Stat1_OrigValue | Stat1_FromDefault | Stat2_Estimated | Stat2_OrigValue | Stat2_FromDefault | Stat3_Estimated | Stat3_OrigValue | Stat3_FromDefault |
+----------+-------+-------+-------+-----------------+-----------------+-------------------+-----------------+-----------------+-------------------+-----------------+-----------------+-------------------+
| 1 | 35 | 2 | 0.213 | true | (null) | (null) | false | false | true | true | 0.212 | false |
| 2 | 513 | 31 | 2.411 | true | 122 | true | true | (null) | true | true | (null) | false |
+----------+-------+-------+-------+-----------------+-----------------+-------------------+-----------------+-----------------+-------------------+-----------------+-----------------+-------------------+
Fiddle: http://www.sqlfiddle.com/#!9/6e84ff2/2/0
I understand how to do the pivot to get the "value" as the field but not how I can include multiple pivots and get them to be named as as Field + _ + OriginalColumn
Edit: The number of distinct values for Field are well known as can be hard-coded into the answer.

SQL DEMO
Because the fields are know you can use conditional aggregation functions
SELECT ObjectId,
MAX(CASE WHEN Field = '''Stat1''' THEN Value END) as Stat1,
MAX(CASE WHEN Field = '''Stat2''' THEN Value END) as Stat2,
MAX(CASE WHEN Field = '''Stat3''' THEN Value END) as Stat3,
MAX(CASE WHEN Field = '''Stat1''' THEN Estimated END) as Stat_Estimated1,
MAX(CASE WHEN Field = '''Stat1''' THEN OrigValue END) as Stat_OrigValue1,
MAX(CASE WHEN Field = '''Stat1''' THEN FromDefault END) as Stat_FromDefault1,
MAX(CASE WHEN Field = '''Stat2''' THEN Estimated END) as Stat_Estimated2,
MAX(CASE WHEN Field = '''Stat2''' THEN OrigValue END) as Stat_OrigValue2,
MAX(CASE WHEN Field = '''Stat2''' THEN FromDefault END) as Stat_FromDefault2,
MAX(CASE WHEN Field = '''Stat3''' THEN Estimated END) as Stat_Estimated3,
MAX(CASE WHEN Field = '''Stat3''' THEN OrigValue END) as Stat_OrigValue3,
MAX(CASE WHEN Field = '''Stat3''' THEN FromDefault END) as Stat_FromDefault3
FROM mytable
GROUP BY ObjectId
;
You can also arrange the data and use the PIVOT function. Be aware value column can only contain one data type so I convert boolean to 0/1
SQL DEMO
SELECT ObjectId, Field, Value
FROM myTable
UNION ALL
SELECT ObjectId, CONCAT(Field, '_Estimated'), CASE WHEN Estimated IS NULL THEN NULL
WHEN Estimated THEN 1
ELSE 0
END
FROM myTable
UNION ALL
SELECT ObjectId, CONCAT(Field, '_OrigValue'), OrigValue
FROM myTable
UNION ALL
SELECT ObjectId, CONCAT(Field, '_FromDefault'), CASE WHEN FromDefault IS NULL THEN NULL
WHEN FromDefault THEN 1
ELSE 0
END
FROM myTable
;

Related

Multi-Class to Multi-Label Transformation in MS SQL Server

I want to transform a Data set of labels to a binary representation via a SQL query, i.e. the following table:
|---------------------------|
| Example | Label |
|---------------------------|
| 1 | Health |
| 1 | Business |
| 1 | Science |
| 2 | Sports |
| 2 | Business |
|---------------------------|
Transforms into a new table:
|---------------------------|-----------|-----------|-----------|
| Example | Business | Health | Science | Sports |
|---------------------------|-----------|-----------|-----------|
| 1 | 1 | 1 | 1 | 0 |
| 2 | 1 | 0 | 0 | 1 |
|-----------|---------------|-----------|-----------|-----------|
via some SQL query. What would be said SQL query?
select example, sum(case when label='Business' then 1 else 0 end) 'Business'
,sum(case when label='Health' then 1 else 0 end) 'Health'
,sum(case when label='Science' then 1 else 0 end) 'Science'
,sum(case when label='Sports' then 1 else 0 end) 'Sports'
From MyTable
group by example

Get count even if the condition doesn't apply

I have two tables
1) Document: which represent a document
+----+----------+------+
| ID | Body | Type |
+----+----------+------+
| 1 | Ramesh | 1 |
| 2 | Khilan | 1 |
| 3 | kaushik | 4 |
| 4 | Chaitali | 2 |
| 5 | Hardik | 2 |
+----+----------+------+
2) Destination: which represent a party of the document
+--------+------------+--------+
| UserId | DocumentId | Status |
+--------+------------+--------+
| 6 | 3 | 4 |
| 4 | 5 | 5 |
| 89 | 2 | 0 |
| 15 | 4 | 3 |
| 89 | 1 | 0 |
+--------+------------+--------+
The status column represent a folder for the user, i want to get the count for each type for each folder, even if the folder is empty for a specifi user,
however if want them in this from,
+--------+--------+--------------+--------------+--------------+
| UserId | Status | Type 1 Count | Type 2 Count | Type 4 Count |
+--------+--------+--------------+--------------+--------------+
| 89 | 0 | 2 | 0 | 0 |
| 89 | 3 | 0 | 0 | 0 |
| 89 | 4 | 0 | 0 | 0 |
| 89 | 5 | 0 | 0 | 0 |
+--------+--------+--------------+--------------+--------------+
the issue I'm facing is I can't find a way to get the types the user does not have by join, i can get them using CASE but not in the form i want
my query is:
`SELECT dd.[Status],
SUM(CASE WHEN d.[Type] = 1 THEN 1 ELSE 0 END) AS 'Type1Count'
SUM(CASE WHEN d.[Type] = 2 THEN 1 ELSE 0 END) AS 'Type2Count'
SUM(CASE WHEN d.[Type] = 4 THEN 1 ELSE 0 END) AS 'Type4Count'
FROM [User] u LEFT JOIN [Destination] dd ON u.[Id] = dd.[UserId]
LEFT JOIN [Document] d ON dd.[DocumentId] = d.[Id]
WHERE u.[Id] = #UserId`
the result is
+--------+--------+--------------+--------------+--------------+
| UserId | Status | Type 1 Count | Type 2 Count | Type 4 Count |
+--------+--------+--------------+--------------+--------------+
| 89 | 0 | 2 | 0 | 0 |
+--------+--------+--------------+--------------+--------------+
So join all users onto a table of all statuses (I have named this Folder as per you description in the question) before you then join to Document and Destination:
SELECT u.UserId, st.Status,
SUM(CASE WHEN doc.Type = 1 THEN 1 ELSE 0 END) AS [Type 1 Count],
SUM(CASE WHEN doc.Type = 2 THEN 1 ELSE 0 END) AS [Type 2 Count],
SUM(CASE WHEN doc.Type = 4 THEN 1 ELSE 0 END) AS [Type 4 Count]
FROM User u
CROSS JOIN Folder st
LEFT OUTER JOIN Destination d
ON d.UserId = u.UserId
AND d.Status = st.Status
LEFT OUTER JOIN Document doc
ON doc.ID = d.DocumentId
GROUP BY u.UserId, st.Status
ORDER BY u.UserId

T-SQL Multiple counts for member types over time

I'm Looking for a way in T-SQL to get counts of members of different types over period of months and years. My Table:
+-------------+-----------+------------
| Member_type | Join_date | End_date |
+-------------+-----------+------------+
| TYPE1 | 12-Nov-07 | 12/11/2015 |
| TYPE2 | 24-Nov-10 | 07/07/2016 |
| TYPE3 | 29-Apr-08 | 28/04/2009 |
| TYPE2 | 28-Apr-06 | 31/03/2007 |
| TYPE1 | 11-Jul-06 | 30/06/2007 |
| TYPE2 | 13-Mar-08 | 12/06/2011 |
+-------------+-----------+------------+
I'm looking for results like this
+-------------+--------+--------+--------+--------+
| Member_type | Jan-15 | Feb-15 | Mar-15 | Apr-15 |
+-------------+--------+--------+--------+--------+
| TYPE1 | 1 | 5 | 4 | 2 |
| TYPE2 | 6 | 4 | 2 | 1 |
| TYPE3 | 5 | 6 | 7 | 8 |
+-------------+--------+--------+--------+--------+
that shows total amount of members of given type during Jan, Feb etc, for recent years.
(those that joined - those that left)
So far I got to this
SELECT COUNT("membership_type") AS JANUARY2015
FROM "dbo"."Data" T0
WHERE (join_date < DATEADD(month, -2, GETDATE()))
AND (join_date > GETDATE())
GROUP BY T0."membership_type" ;
SELECT Member_type
, COUNT(CASE WHEN YEAR(join_date) = 2015 AND MONTH(join_date) = 1 THEN 1 END) AS [Jan-15]
, COUNT(CASE WHEN YEAR(join_date) = 2015 AND MONTH(join_date) = 2 THEN 1 END) AS [Feb-15]
, COUNT(CASE WHEN YEAR(join_date) = 2015 AND MONTH(join_date) = 3 THEN 1 END) AS [Mar-15]
, COUNT(CASE WHEN YEAR(join_date) = 2015 AND MONTH(join_date) = 4 THEN 1 END) AS [Apr-15]
...
FROM MarketingData
GROUP BY Member_type

Group by and count apperances in other column

My table look like this:
----------------------------------------------
|id | action | building | date |
----------------------------------------------
|1 | IN | 1000 | 01-01-2015 |
|2 | OUT | 1000 | 01-01-2015 |
|3 | OUT | 1000 | 05-01-2015 |
|4 | IN | 2000 | 01-01-2015 |
----------------------------------------------
I would like to group the result by building and count the how many IN and OUT actions exists. Data and id doesn't matter in the result. The result should be like:
-------------------------
| Building | IN | OUT |
-------------------------
| 1000 | 1 | 2 |
| 2000 | 1 | 0 |
-------------------------
The action column can only contain IN and OUT.
My best attempt is:
select distinct (action), building, count(*)
from table
group by action, building
Output:
-------------------------------------
| action | Building | count(*) |
-------------------------------------
| IN | 1000 | 1 |
| OUT | 1000 | 2 |
| IN | 2000 | 1 |
-------------------------------------
Do it with conditional aggregation:
select Building,
sum(case when action = 'IN' then 1 else 0 end) as [IN],
sum(case when action = 'OUT' then 1 else 0 end) as [OUT],
from TableName
group by Building
You need to use conditional aggregation:
select building,
count(CASE WHEN action = 'IN' THEN 1 END) AS 'IN',
count(CASE WHEN action = 'OUT' THEN 1 END) AS 'OUT'
from table
group by building

SQL Server 2012 Rows into Columns with varchar

I am looking for a solution for the following problem, which affects two tables. I already tried to search for the solution, but couldn't find the way to go.
single_value
| docId | siteNo | siteName | siteAccount | comment | docDate | extNo
---+------------+--------+---------------+-------------+---------+-------------------------+-------
1 | T000000095 | 201060 | Main Location | 92400 | NULL | 2014-10-31 00:00:00.000 | NULL
multi_value
| docId | field_no | row_no | value_char | value_date | value_num
---+------------+----------+--------+------------+------------+-----------
1 | T000000095 | 60 | 1 | NULL | NULL | 250.00
2 | T000000095 | 60 | 2 | NULL | NULL | -1.24
3 | T000000095 | 61 | 1 | Positive | NULL | NULL
4 | T000000095 | 61 | 2 | Negative | NULL | NULL
5 | T000000095 | 62 | 1 | NULL | NULL | 90000.00
6 | T000000095 | 62 | 2 | NULL | NULL | 688000.00
What I need is now an SQL statement which gives me an output like the following one for each row_no of the table multi_value for a specific docId:
| docId | siteNo | siteName | siteAccount | comment | docDate | extNo | amount | addInfo | costUnit
---+------------+--------+---------------+-------------+---------+-------------------------+-------|--------+----------+----------
1 | T000000095 | 201060 | Main Location | 92400 | NULL | 2014-10-31 00:00:00.000 | NULL | 250.00 | Positive | 90000.00
2 | T000000095 | 201060 | Main Location | 92400 | NULL | 2014-10-31 00:00:00.000 | NULL | -1.24 | Negative | 688000.00
It has to list all Information of the table 'single_value' and kind of transpose the values of the table 'multi_value'. The connection between both tables can be achieved via the docId.
The table 'multi_value' is designed in that way, that each field no only allows one specific column to be filled:
60 = value_num (amount)
61 = value_char (addInfo)
62 = value_num (costUnit)
What is the easiest way to achieve that? The table layout cannot be changed. For the transpose of the multi_value I already tried the following, but it doesn't work with the varchar datatype within the column value_char.
SELECT row_no
SUM(case when field_no = 60 then value_num else 0 end) as amount,
--(case when field_no = 61 then value_char else 0 end) as addInfo,
SUM(case when field_no = 62 then value_num else 0 end) as costUnit
FROM multi_value
WHERE docId = 'T000000095'
GROUP By
row_no
Many thanks in advance.
Thomas
Not very elegant, but works fine :)
SELECT row_no,
SUM(case when field_no = 60 then value_num else 0 end) as amount,
case SUM(case
when field_no = 61 then (
case value_char
when 'Positive' then 1
else 2 end
) else 0 end
) when 1 then 'Positive' else 'Negative' end as addInfo,
SUM(case when field_no = 62 then value_num else 0 end) as costUnit
FROM multi_value
GROUP By
row_no
UPDATE
WITH dist as
(
SELECT DISTINCT value_char
FROM multi_value
WHERE value_char is not null
)
, with_no as
(
select value_char, row_number() over(order by value_char) [no]
from dist
)
, [raw] as
(
SELECT m.row_no,
SUM(case when m.field_no = 60 then m.value_num else 0 end) as amount,
SUM(case when m.field_no = 61 then d.[no] else 0 end) as addInfo,
SUM(case when m.field_no = 62 then m.value_num else 0 end) as costUnit
FROM multi_value m
LEFT JOIN with_no as d on d.value_char = m.value_char
GROUP By
m.row_no
)
SELECT row_no
, amount
, d.value_char as addInfo
, costUnit
FROM [raw] r
LEFT JOIN with_no as d on d.[no] = r.addInfo

Resources