Using TSQL to UNPIVOT simple aggregated totals...is this even possible? - sql

I was trying to perform a simple un-pivoting of some data before it is loaded into Microsoft PowerBI. Since the PowerBI report has to use DirectQuery, using 'Unpivot' in the query editor is not an option. So it seemed that this could probably be done in the intial SQL that gets loaded.
select
sum(case when cw.State = 'WORK' then 1 else null end) [Work]
,sum(case when cw.State = 'OUT' then 1 else null end) [Out]
,sum(case when cw.State = 'POST' then 1 else null end) [Post]
from CurrentWork cw
This code outputs:
Work Out Post
---- --- ----
5 3 21
But I would like the output to be displayed like:
Event Amount
----- ------
Work 5
Out 3
Post 21
I believe I need to use the UNPIVOT TSQL command, but cant figure out the correct way to use it.
Is this even possible, or am I approaching this problem from the wrong direction?

You don't need to do UNPIVOT, you want aggregation :
select status, count(*)
from CurrentWork
group by status;
If above data are aggregated then you can use either subuqery or cte with apply :
with t as (
select sum(case when cw.State = 'WORK' then 1 else null end) [Work]
sum(case when cw.State = 'OUT' then 1 else null end) [Out]
sum(case when cw.State = 'POST' then 1 else null end) [Post]
from CurrentWork cw
)
select tt.Event, tt.[Amount]
from t cross apply
( values ([Work], [Amount]), ([Out], [Amount]), ([Post], [Amount])
) tt(Event, [Amount]);

Related

How to get output in sql two rates into two column in same table

Please help on this. Sometimes my title maybe wrong. Actually i'm unable to explain the problem in word. See below images. Image 1 is db table structure. Image 2 is what I expect result.
I used mentioned query and got result as below image. Also I need to remove 'NULL's and same URGENT_LEVEL values in one row. How i do that? Using ms-sql server.
select TRACKING_NUMBER,URGENT_LEVEL,
case when FROM_KM = '0' then Charge end as 'Under1km' ,
case when FROM_KM='1' then Charge end as '1-100KM'
from my_table
where TRACKING_NUMBER = 'TEST001'
After query
You can use conditional aggregation:
select tracking_number, urgent_level,
sum(case when to_km - from_km <= 1 then charge else 0 end) as charge_under_1,
sum(case when to_km - from_km > 1 and to_km - from_km <= 100 then charge else 0 end) as charge_1_100
from t
group by tracking_number, urgent_level;
I don't know which database you are using, but you need to use group by clause, and something similar to case expression (in oracle) as below :
select TRACKING_NUMBER
, URGENT_LEVEL
, max(case when FROM_KM = 0 and TO_KM = 1 then CHARGE end) as "UNDER 1KM"
, max(case when FROM_KM = 1 and TO_KM = 100 then CHARGE end) as "1-100KM"
from your_table
where TRACKING_NUMBER = 'TEST001'
group by TRACKING_NUMBER, URGENT_LEVEL
order by "UNDER 1KM"
;

Don't understand query behaviour

I have a strange behaviour in report builder.
I'm working from an existing dataset and testing my own code in SQL studio before trying in report builder. And I'm lost because I don't understand why the following doesn't work :
SELECT
v_Collection_Alias.Name as CollectionName,
v_Package_Alias.Name as SoftwareName,
'Package' as ApplicationType,
NumberSuccessTable='NumberSuccessTable', sum(case when stat.LastState in (-1,13) then 1 else 0 end) as NumberSuccess,
NumberInProgressTable='NumberInProgress', sum(case when stat.LastState in (8,9) then 1 else 0 end) as NumberInProgress,
NumberUnknownTable='NumberUnknown', sum(case when stat.LastState in (0) then 1 else 0 end) as Unknown,
NumberErrorTable='NumberError', sum(case when stat.LastState in (11) then 1 else 0 end) as NumberError,
NumberOtherTable='NumberOther', sum(case when stat.LastState in (10) then 1 else 0 end) as NumberOther,
'' as LastModifiedby,
'' as Version,
v_Advertisement_Alias.CollectionID as CollectionID,
v_Advertisement_Alias.AdvertisementID as DeploymentID,
'' as CI_ID,
'' as DeploymentTime,
v_Advertisement_Alias.PresentTime as ModificationTime,
'' as AssignmentID
FROM fn_rbac_Advertisement(#UserSIDs) v_Advertisement_Alias
JOIN fn_rbac_ClientAdvertisementStatus(#UserSIDs) stat on v_Advertisement_Alias.AdvertisementID = stat.AdvertisementID
INNER JOIN fn_rbac_Package2(#UserSIDs) v_Package_Alias ON v_Advertisement_Alias.PackageID = v_Package_Alias.PackageID
INNER JOIN fn_rbac_Collection(#UserSIDs) v_Collection_Alias ON v_Advertisement_Alias.CollectionID = v_Collection_Alias.CollectionID
This in report builder is prompting me an error because fn_rbac_Advertisement.Name need a group by clause. Whereas the following is properly working in the original report :
SELECT
v_Collection_Alias.Name as CollectionName,
v_Package_Alias.Name as SoftwareName,
'' as ApplicationType,
'' as NumberSuccess,
'' as NumberInProgress,
'' as NumberUnknown,
'' as NumberErrors,
'' as NumberOther,
'' as LastModifiedby,
'' as Version,
v_Advertisement_Alias.CollectionID as CollectionID,
v_Advertisement_Alias.AdvertisementID as DeploymentID,
'' as CI_ID,
'' as DeploymentTime,
v_Advertisement_Alias.PresentTime as ModificationTime,
'' as AssignmentID,
'' as ApplicationType
FROM fn_rbac_Advertisement(#UserSIDs) v_Advertisement_Alias
INNER JOIN fn_rbac_Package2(#UserSIDs) v_Package_Alias ON v_Advertisement_Alias.PackageID = v_Package_Alias.PackageID
INNER JOIN fn_rbac_Collection(#UserSIDs) v_Collection_Alias ON v_Advertisement_Alias.CollectionID = v_Collection_Alias.CollectionID
and the following returns me what I want in SQL Studio :
Select
NumberSuccessTable='NumberSuccessTable', sum(case when stat.LastState in (-1,13) then 1 else 0 end),
NumberInProgressTable='NumberInProgress', sum(case when stat.LastState in (8,9) then 1 else 0 end),
NumberUnknownTable='NumberUnknown', sum(case when stat.LastState in (0) then 1 else 0 end) AS NumberU,
NumberErrorTable='NumberError', sum(case when stat.LastState in (11) then 1 else 0 end) AS NumberError,
NumberOtherTable='NumberOther', sum(case when stat.LastState in (10) then 1 else 0 end) as NumberOther
From v_ClientAdvertisementStatus stat
Thanks for any help guys ! :)
First Query:
when you use aggregate function like SUM you need to do a GROUP BY on all your columns that Don't have the aggregate function.
Second Query:
this works because you dont have an aggregate function in your select therefor you dont have to use GROUP BY and report builder can handle the sum and group by for you.
Third query:
this query works because you have aggregate function on all your columns so you dont need to GROUP BY.
Always remember the LOGICAL query processing order is as below:
1.FROM
2.WHERE
3.GROUP BY
4.HAVING
5.SELECT
6.ORDER BY
so your GROUP BY happens before SELECT for that reason when you group by you cant include '' in your group by clause because that column does not exists yet.
so it gives you an error.
When using aggregate functions (such as SUM) you must apply a GROUP BY clause to all columns that are not aggregated eg:
GROUP BY v_Collection_Alias.Name,
v_Package_Alias.Name,
v_Advertisement_Alias.CollectionID as CollectionID,
v_Advertisement_Alias.AdvertisementID as DeploymentID,
v_Advertisement_Alias.PresentTime as ModificationTime
Alternatively, if you don't want to maintain a lengthy GROUP BY clause you could remove the SUM functions from the SELECT statement and let the report handle the aggregation, with appropriate grouping in Report Builder.

What does a multiple count query in SQL return?

I have a product table and every product might be delivered, idle, shipping, preparing.
I want to show a list with the counts of products for each state, and I can see how to query for that here:
How to get multiple counts with one SQL query?
However, what does this query return, and how do I assign the return value to lets say, 4 integers, called deliveredCount, idleCount, shippingCount, preparingCount?
PS: For the record, I am using SQLite with OrmLite in Android with JAVA
EDIT: In this SO question people explain what Query to do when you want to get multiple counts, but they don't tell us what does that query return and in what format. For example:
SELECT a.distributor_id,
(SELECT COUNT(*) FROM myTable WHERE level='personal' and distributor_id = a.distributor_id) as PersonalCount,
(SELECT COUNT(*) FROM myTable WHERE level='exec' and distributor_id = a.distributor_id) as ExecCount,
(SELECT COUNT(*) FROM myTable WHERE distributor_id = a.distributor_id) as TotalCount
FROM myTable a ;
What is the return type of this and what is the format?
PS2: Someone was really quick to downvote my question because it lacked sufficient information. Then I edited it, but the downvote still remains :(
Hard to say for sure but sounds like you need to use a version of the top answer in the link you have provided.
Something like;
SELECT ProductID,
COUNT(*) AS Total,
SUM(CASE WHEN pStatus = 'delivered' THEN 1 ELSE 0 END) DeliveredCount,
SUM(CASE WHEN pStatus = 'idle' THEN 1 ELSE 0 END) IdleCount,
SUM(CASE WHEN pStatus = 'shipping' THEN 1 ELSE 0 END) ShippingCount,
SUM(CASE WHEN pStatus = 'preparing' THEN 1 ELSE 0 END) PreparingCount
FROM ProductTable
GROUP BY ProductID
This will return something like;
ProductID | DeliveredCount | IdleCount | ...
1 | 250 | 3250 | ...
You might want to try this.
SELECT
SUM(CASE WHEN Prod = 'delivered' THEN 1 ELSE 0 END) as deliveredCount,
SUM(CASE WHEN Prod = 'idle' THEN 1 ELSE 0 END) as idleCount,
SUM(CASE WHEN Prod = 'shipping' THEN 1 ELSE 0 END) as shippingCount,
SUM(CASE WHEN Prod = 'preparing' THEN 1 ELSE 0 END) as preparingCount
FROM Product
select
concat(state, "Count"),
count(*)
from product
group by state
Which would return 4 rows (assuming four unique values of state):
fooCount | 15
etc

SQL Multiple Rows to Single Row Multiple Columns

I am including a SQLFiddle to show as an example of where I am currently at. In the example image you can see that simply grouping you get up to two lines per user depending on their status and how many of those statuses they have.
http://sqlfiddle.com/#!3/9aa649/2
The way I want it to come out is to look like the image below. Having a single line per user with two totaling columns one for Fail Total and one for Pass Total. I have been able to come close but since BOB only has Fails and not Passes this query leaves BOB out of the results. which I want to show BOB as well with his 6 Fail and 0 Pass
select a.PersonID,a.Name,a.Totals as FailTotal,b.Totals as PassTotals from (
select PersonID,Name,Status, COUNT(*) as Totals from UserReport
where Status = 'Fail'
group by PersonID,Name,Status) a
join
(
select PersonID,Name,Status, COUNT(*) as Totals from UserReport
where Status = 'Pass'
group by PersonID,Name,Status) b
on a.PersonID=b.PersonID
The below picture is what I want it to look like. Here is another SQL Fiddle that shows the above query in action
http://sqlfiddle.com/#!3/9aa649/13
Use conditional aggregation if the number of values for status column is fixed.
Fiddle
select PersonID,Name,
sum(case when "status" = 'Fail' then 1 else 0 end) as failedtotal,
sum(case when "status" = 'Pass' then 1 else 0 end) as passedtotals
from UserReport
group by PersonID,Name
Use conditional aggregation:
select PersonID, Name,
sum(case when Status = 'Fail' then 1 else 0 end) as FailedTotal,
sum(case when Status = 'Pass' then 1 else 0 end) as PassedTotal
from UserReport
group by PersonID, Name;
With conditional aggregation:
select PersonID,
Name,
sum(case when Status = 'Fail' then 1 end) as Failed,
sum(case when Status = 'Passed' then 1 end) as Passed
from UserReport
group by PersonID, Name

nested SQL queries on one table

I am having trouble formulating a query to get the desired output.
This query involves one table and two columns.
First column bld_stat has 4 different values Private, public, Public-Abandoned, Private-Abandoned the other column bld_type, single_flr, multi_flr, trailer, Whs.
I need to get results that look like this:
So far I can get the first two columns but after that I have not been able to logically get a query to work
SELECT bld_stat, COUNT(grade) AS single_flr
FROM (SELECT bld_stat,bld_type
FROM bld_inventory WHERE bld_type = 'single_flr') AS grade
GROUP BY bld_stat,bld_type,grade
The term you are going for is pivoting. I think this should work...no need for the subquery, and I've changed your group by to only bld_stat
SELECT bld_stat,
sum(case when bld_type = 'singl_flr' then 1 else 0 end) AS single_flr,
sum(case when bld_type = 'multi_flr' then 1 else 0 end) AS multi_flr,
sum(case when bld_type = 'trailer' then 1 else 0 end) AS trailer,
sum(case when bld_type = 'whs' then 1 else 0 end) AS WHS
FROM bld_inventory
GROUP BY bld_stat