I want to write a summary query and present the results in a single table - sql

I need to write a summary report on 3 different status values, with a count and an amount column for each status, with the results presented in a single table. For example, the output would look like this:
The query to produce each line of code (in an individual output) is:
select case when status_key = '2' then 'Paid' else '' end as 'Status'
, COUNT(BillNo) as [Count]
, SUM(amtpd) as [Amount Paid]
from billtable
where client = 101
and status_key = '2'
group by status_key
select case when status_key = '1' then 'Queued' else '' end as 'Status'
, COUNT(BillNo) as [Count]
, SUM(amtpd) as [Amount Paid]
from billtable
where client = 101
and status_key = '1'
group by status_key
select case when status_key = '4' then 'Hold' else '' end as 'Status'
, COUNT(BillNo) as [Count]
, SUM(amtpd) as [Amount Paid]
from billtable
where client = 101
and status_key = '4'
group by status_key
This produces three results like:
I am using SQL Server database and SSMS to develop the query.

No need for union.
Use WHERE to filter to only the status_keys that you want, then expand you CASE statement to re-code from a number to a word.
select
case when status_key = '2' then 'Paid'
when status_key = '1' then 'Queued'
when status_key = '4' then 'Hold'
else 'Error!' end AS [Status],
COUNT(BillNo) AS [Count],
SUM(amtpd) AS [Amount Paid]
from
billtable
where
client = 101
AND status_key IN ('1','2','4')
group by
status_key
EDIT Modified example using a dimension table
select
status.description AS [Status],
COUNT(bill_table.BillNo) AS [Count],
SUM(bill_table.amtpd) AS [Amount Paid]
from
billtable
inner join
status
on billtable.status_key = status.key
where
bill_table.client = 101
AND bill_table.status_key IN ('1','2','4')
group by
status.description
You can then have a foreign key constraint from status to billtable. This will ensure that data can not be inserted into billtable unless there is a corresponding key in status.
Your lookups will then always work. But at the 'cost' of inserts failing if the status table has not been correctly populated.
This fact-table and dimension-table construction is the underpinning of relational database design.

you just have to union all
your first query
Union all
your Second query
union all
your third query

Just add UNION between your queries.

Related

Join 2 Queries and a Pivot in SQL

I have 2 queries that I want to join together. They come from the same table, but the organization and structure of columns is bad. Hence the need for a Pivot in Query 1, then join it to Query 2.
I do know that this needs to be done with a CTE, but my output either results in a blank query, or info in the wrong columns.
I'm using SSMS18. Thanks for the help.
Query 1
Select *, '' AS 'DNC', '' AS 'DNF', '' AS 'DNR' from
(Select
Opin,
Qty,
[TicketDate],
[TicketNumber],
[Cust],
[ProdID]
from [dbo].[obser]) AS ID
Pivot (
SUM([Qty])
For [Opin] IN ([ArvUnits],[DeliveryUnits])
) AS SingleOTRow
where
TicketDate between '2021-01-01' and '2021-03-15'
and ProdID like '%_LB%'
Query 2
select
IOA.Cust, IOA.TicketNumber, IOA.TicketDate, '' AS 'ProdID','' AS 'IOA QTY', '' AS 'Delivery Units', IOA.ProdID AS 'DNC',
case
When IOA.Qty >=1 Then '1'
else 'N'
end AS 'DNF',
CASE
when IOA.ProdID = '21' Then 'Full'
else 'Not a Valid DNC- Review'
END as 'DNR'
from [dbo].[obser] AS IOA
where
IOA.TicketDate between '2021-01-01' and '2021-03-15' and
IOA.Opin= 'DNS'
Sample Data
[Sample Data]

Deriving values based on result of the query and grouping the data

I am writing a sql where I am trying to pull out information of the status of the courses the user has enrolled. I am trying to return single record for each user. Two fields in the select list would derive the value in the following manner
CourseResultStatusId -
If the status of all the courses is passed then return the status as passed otherwise.
If any status is fail, the overall status is fail.
If any of the status is expired, then overall status is expired.
If any of the status is in-progress then overall status is in-progress
ExpiryDateTime - Training expiring (nearest date)
I need to apply the following logic on courses he has been assigned.
cr.ExpiryDateTime > GetDate() and cr.ExpiryDateTime <= dateadd(dd,30,getdate()) )
If you see below , the query I have written so far pulls the courses that each user has been enrolled but it is not a cumulative result. Do I need to group, if yes would need help.
DECLARE #Rep1 INT;
SET #Rep1 = 13119;
SELECT
cr.[CourseID]
,cr.[UserID]
,u.[Code]
,u.[DisplayName]
,t.[Name]
,cr.[CourseResultStatusID] AS [CourseResultStatusID]
,crs.[Description] AS [CourseResultStatusDescription]
,c.[PointsRequired]
,cr.[ExpiryDateTime]
FROM [training].[CourseResult] cr
INNER JOIN [training].[Course] c
ON cr.[CourseID] = c.[ID] and c.[IsOptional] = 0 -- and cr.ExpiryDateTime > GetDate() and cr.ExpiryDateTime <= dateadd(dd,30,getdate())
INNER JOIN [training].[CourseResultStatus] crs
ON cr.[CourseResultStatusID] = crs.[ID]
INNER JOIN org.RepresentativeTierHistory rth on rth.RepresentativeID = cr.[UserID] and GetDate() between rth.StartDate and rth.EndDate
INNER JOIN org.tier t on t.ID = rth.TierID
LEFT JOIN [org].[User] u
ON u.[ID] = cr.[UserID]
WHERE cr.[UserID] IN (
SELECT hd.DescendantId FROM org.HierarchyDescendant hd WHERE hd.RepresentativeId = #Rep1 UNION ALL SELECT #Rep1 -- for management exchange info
)
order by UserID
The result of the query is as follows. I have circled to show you records that belong to a particular user and the columns that I am interested in . I need help in getting single record for each user based on the of logic that I mentioned above.
If I followed you correctly, you can implement the priorization rules on the overall result of each user using conditional aggregation.
Starting from your existing query, the logic would be:
select
cr.[UserID],
case
when min(case when crs.[Description] = 'Complete' then 1 else 0 end) = 1
then 'Complete'
when max(case when crs.[Description] = 'Fail' then 1 else 0 end) = 1
then 'Fail'
when max(case when crs.[Description] = 'Expired' then 1 else 0 end) = 1
then 'Expired'
when max(case when crs.[Description] = 'In Progress' then 1 else 0 end) = 1
then 'In Progress'
end as ResultStatus
from ...
where ...
group by cr.[UserID]
As for the date filtering logic, you should be able to implement it directly in the where clause.
It is possible that other parts of your query can be optimized - you might want to ask a new question for this, providing proper sample data and desired results.

Subqueries in MSSQL producing NULL values

I am trying to determine my store only accounts revenue from the database, to do this I need to look through all account numbers with revenue against a 'store' description who do NOT appear in a list of accounts with an 'online' description which I have tried todo in the subquery below. The query runs however it just returns NULL values in my store_only_revenue column. Any guidance on what to do from here would be appreciated. Am I approaching the problem in a good way? Or is there a better solution:
SELECT
town,
financial_pd as month,
SUM(CASE WHEN [Descr] = 'online' THEN Net_Revenue ELSE 0 END) as online_revenue,
SUM(CASE WHEN [Descr] = 'store' THEN Net_Revenue ELSE 0 END) as store_revenue,
COUNT(DISTINCT CASE WHEN [Descr] = 'online' THEN Account_Number ELSE NULL END) as online_accounts,
COUNT(DISTINCT CASE WHEN [Descr] = 'store' THEN Account_Number ELSE NULL END) as store_accounts,
(SELECT
SUM(Net_Revenue)
FROM [mydb].[dbo].[mytable]
WHERE
Descr = 'store'
AND Account_Number
NOT IN(
SELECT DISTINCT Account_Number
FROM [mydb].[dbo].[mytable]
WHERE
Descr = 'online')
) as store_only_revenue
FROM [mydb].[dbo].[mytable] as orders
WHERE
Group_name = 'T'
AND NOT
Type_name_1 = 'Electronic'
AND
Account_type <> 1
AND
Total_Value > 0
AND
(Insert_Date BETWEEN '2016-05-30' AND '2016-07-03'
OR
Insert_Date BETWEEN '2015-05-25' AND '2015-06-28')
OR
(Insert_Date BETWEEN '2016-05-30' AND '2016-07-03'
AND
Insert_Date BETWEEN '2015-05-25' AND '2015-06-28')
GROUP BY
town,
financial_pd as period
This expression is suspect:
Account_Number NOT IN (SELECT DISTINCT t.Account_Number
FROM [mydb].[dbo].mytable t
WHERE t.Descr = 'online'
)
Assuming that the syntax problems are typos (missing table name, desc is a reserved word), then this will never return true if even one Account_Number is NULL. One way to fix this is:
Account_Number NOT IN (SELECT t.Account_Number
FROM [mydb].[dbo].mytable t
WHERE t.Desc = 'online' AND t.Account_Number IS NOT NULL
)
I would use NOT EXISTS:
not exists (select 1
from [mydb].[dbo].??? x
where x.Desc = 'online' AND ??.Account_Number = x.Account_Number
)
You need to use proper table aliases for this to work. Either of these solutions may fix your problem.

syntax error case statement

I am trying to write an sql statement but i am getting syntax error. I know it is do with my select and case statement but cant figure out.As the error is not descriptive. I am using redshift
select school_district_teacher_ind,customer_status,initial_pay_type,(select(
CASE
WHEN total_line_price = 0
THEN 'free'
ELSE 'paid'
END
)
from storiacloud.schl_storia_revenue_fact_a)as a,count(distinct convert(varchar(100),[Otc_Order_Number])+'_'+ convert(varchar(100),[Otc_Order_Line_Number]))
from storiacloud.schl_storia_revenue_fact_a as fact
inner join
storiacloud.schl_storia_school_status as status
on fact.school_ucn = status.ucn
where date = '11/2/2015'
group by school_district_teacher_ind,customer_status,initial_pay_type,a
Below is the error
ERROR: Invalid Query:
Detail:
-----------------------------------------------
error: Invalid Query:
code: 8001
context: single-row subquery returns more than one row
query: 5132289
location: 25.cpp:69
process: padbmaster [pid=29183]
-----------------------------------------------
Execution time: 0.16s
1 statement failed.
The results that i expect are
Note first column customer type is school_district_teacher_ind in the above select statment
Your case was a subquery that is selecting from the same table as your main query. Try this;
SELECT school_district_teacher_ind ,
customer_status ,
initial_pay_type ,
CASE WHEN total_line_price = 0 THEN 'free'
ELSE 'paid'
END AS a ,
COUNT(DISTINCT CONVERT(VARCHAR(100), [Otc_Order_Number]) + '_'
+ CONVERT(VARCHAR(100), [Otc_Order_Line_Number]))
FROM storiacloud.schl_storia_revenue_fact_a AS fact
INNER JOIN storiacloud.schl_storia_school_status AS status ON fact.school_ucn = status.ucn
WHERE date = '11/2/2015'
GROUP BY school_district_teacher_ind ,
customer_status ,
initial_pay_type ,
CASE WHEN total_line_price = 0 THEN 'free'
ELSE 'paid'
END
I think you just want conditional aggregation. The query is something like this:
select school_district_teacher_ind, customer_status, initial_pay_type,
sum(case when total_line_price = 0 then 1 else 0 end) as free,
sum(case when total_line_price = 0 then 0 else 1 end) as paid
from storiacloud.schl_storia_revenue_fact_a fact inner join
storiacloud.schl_storia_school_status status
on fact.school_ucn = status.ucn
where date = '2015-11-02'
group by school_district_teacher_ind,customer_status, initial_pay_type;

calculating completed task ratio

I've a table with NAMES and STATUS with C(completed) and N(not completed) status. I want check how many tasks are not completed for each name. I tried the following code and it is returning all '0' values:
select name, (select count(status) from alteon where status= 'n') / (select count(status) from alteon) from alteon group by name;
I'm expecting the result as not completed / total assigned where total assigned = complete+not completed.
as mentioned earlier, I'm getting value as '0' beside each employee name.
I think the following query does what you want:
select name,
sum(case when status = 'n' then 1 else 0 end) as n_status,
avg(case when status = 'n' then 1.0 else 0 end) as n_status_ratio
from alteon;
Here is the query which gives the result as you explained above.
select count(status)as Total_assigned,
sum(IF(status='n', 1, 0)) as Not_completed,name
from alteon group by name ;
Here is the sqlfiddle
You don't have to use multiple select statements. Use CASE to count the incomplete tasks.
select name, count(case when status = 'n' then 1 else null end)/count(status)
from alteon
group by name;
sqlfiddle.