Columns created by case statement do not group by - sql-server

Here is a T-SQL query:
SELECT
A.DateStamp,
CASE WHEN A.T = 10 THEN A.counts END AS HT,
CASE WHEN A.T = 98 THEN A.counts END AS BP,
CASE WHEN A.T = 94 THEN A.counts END AS MP,
CASE WHEN A.T = 12 THEN A.counts END AS SP
FROM
A
WHERE
(A.date_time BETWEEN GETDATE() - 60 AND GETDATE() - 30) -- say
--GROUP BY A.DateStamp,A.T,A.counts
ORDER BY
CONVERT(DATE, A.DateStamp) ASC
It works well. Previously I was using multiple copies of table A and all joined. over here results are correct but split in multiple rows like:
date | BP | MP | SP | HT |
-----------+----+----+----+----+
22/10/2017 12 34 56 78
Looks Like -- -- -- --
22/10/2017 12 -- -- --
22/10/2017 -- 34 -- --
22/10/2017 -- -- 56 --
22/10/2017 -- -- -- 78

You need to use conditional aggregation for this and GROUP BY just the DateStamp field:
SELECT A.DateStamp,
SUM(CASE WHEN A.T=10 THEN A.counts ELSE 0 END) AS HT,
SUM(CASE WHEN A.T=98 THEN A.counts ELSE 0 END) AS BP,
SUM(CASE WHEN A.T=94 THEN A.counts ELSE 0 END) AS MP,
SUM(CASE WHEN A.T=12 THEN A.counts ELSE 0 END) AS SP
FROM A
WHERE (A.date_time BETWEEN getdate()-60 AND getdate()-30)
GROUP BY A.DateStamp
ORDER BY convert(date,A.DateStamp) ASC

i guess you need to group by date only
SELECT convert(date,A.DateStamp) AS Date,
SUM(CASE WHEN A.T=10 THEN A.counts END) AS HT ,
SUM(CASE WHEN A.T=98 THEN A.counts END) AS BP,
SUM(CASE WHEN A.T=94 THEN A.counts END) AS MP,
SUM(CASE WHEN A.T=12 THEN A.counts END) AS SP
FROM A
WHERE A.date_time BETWEEN getdate()-60 AND getdate()-30
GROUP BY convert(date,A.DateStamp)
ORDER BY convert(date,A.DateStamp) ASC

Related

How can separate morning and evening shifts patients with gender wise using SQL stored procedure

I want to separate the morning and evening patient checked-in count gender-wise and also if gender age less then show the Female child and M child on the basis of gender.
SELECT
COUNT(ch.EnteredOn) AS counter,
CONVERT(date, ch.EnteredOn) AS sessionDay,
SUM(CASE WHEN CAST(CONVERT(CHAR(2), ch.EnteredOn, 108) AS INT) < 12 THEN 1 ELSE 0 END) AS 'Morning',
SUM(CASE WHEN CAST(CONVERT(CHAR(2), ch.EnteredOn, 108) AS INT) >= 12 THEN 1 ELSE 0 END) AS 'Evening',
SUM(CASE WHEN p.Gender = 1 THEN 1 ELSE 0 END) AS Male,
SUM(CASE WHEN p.Gender = 2 THEN 1 ELSE 0 END) AS Fmale,
SUM(CASE WHEN DATEDIFF(hour, P.DOB, GETDATE()) / 8766 <= 18
AND P.Gender = 1 THEN 1 ELSE 0 END) AS MChild,
SUM(CASE WHEN DATEDIFF(hour, P.DOB, GETDATE()) / 8766 <= 18
AND P.Gender = 2 THEN 1 ELSE 0 END) AS FChild
FROM
Patient.CheckIn ch
INNER JOIN
Patient.Patients P ON ch.PatientId = p.PatientId
GROUP BY
Ch.EnteredOn
I have only three columns like Gender, Time and DOB. Gender id 1 shows the male and Gender id 2 is using for females.
enter image description here
SELECT convert(date, ch.EnteredOn) AS sessionDay, CAST( CONVERT(CHAR(2), ch.EnteredOn, 108) AS INT) <12 as morning, count(ch.EnteredOn) AS counter
,sum(case when p.Gender=1 Then 1 ELSE 0 end) as Male
,sum(case when p.Gender=2 Then 1 ELSE 0 end) as Fmale
,Sum( case when DATEDIFF(hour,P.DOB,GETDATE())/8766<=18 AND P.Gender=1 Then 1 ELSE 0 end ) as MChiled
,Sum( case when DATEDIFF(hour,P.DOB,GETDATE())/8766<=18 AND P.Gender=2 Then 1 ELSE 0 end ) as FChiled
FROM Patient.CheckIn ch
inner join Patient.Patients P on ch.PatientId=p.PatientId
Group by Ch.EnteredOn
Group BY can be
Group by convert(date, ch.EnteredOn) AS sessionDay, CAST( CONVERT(CHAR(2), ch.EnteredOn, 108) AS INT) <12

How to calculate Amount by days wise of customer in sql

i want to calculate Total amount of customer by day wise from Invoice Table for that i used below query : what i want
1st Table Name : MstCustomer
2nd Table Name : Job_Details
3rd Table Name : Invoice
select distinct Cust.Cust_Name,
case when DATEDIFF(dd,INV.Bill_Submit_Date,GETDATE()) > 0 and DATEDIFF(dd,INV.Bill_Submit_Date,GETDATE()) < 31
then sum(INV.Bill_Amount) end AS '1-30 DAYS',
case when DATEDIFF(dd,INV.Bill_Submit_Date,GETDATE()) > 30 and DATEDIFF(dd,INV.Bill_Submit_Date,GETDATE()) < 46
then sum(INV.Bill_Amount) end AS '31-45 DAYS',
case when DATEDIFF(dd,Bill_Submit_Date,GETDATE()) > 45
then sum(INV.Bill_Amount) end AS 'ABOVE 45 DAYS',
Balance =sum(INV.Bill_Amount) - sum(INV.Advance_Amount)
from Invoice INV
inner join Job_Details JD on JD.Job_ID= INV.Job_ID
inner join MstCustomer cust ON cust.Cust_ID= JD.Cust_ID
group by Cust.Cust_Name,Bill_Submit_Date,Bill_Amount,Advance_Amount
Expected Result
By doing the DATEDIFF in a subquery you can simplify the calculations in the outer query.
And you'd want to SUM the CASE, instead of using the SUM in a CASE.
For example:
select
cust.Cust_Name as "Customer Name",
SUM(CASE WHEN q.Days_Submitted BETWEEN 1 AND 30 THEN q.Bill_Amount END) AS "1-30 Days Amount",
SUM(CASE WHEN q.Days_Submitted BETWEEN 31 AND 45 THEN q.Bill_Amount END) AS "31-45 Days Amount",
SUM(CASE WHEN q.Days_Submitted > 45 THEN q.Bill_Amount END) AS "Above 45 Days Amount",
SUM(q.Bill_Amount - q.Advance_Amount) AS Balance
from
(
select
JD.Cust_ID,
DATEDIFF(dd, INV.Bill_Submit_Date, GetDate()) as Days_Submitted,
SUM(INV.Bill_Amount) as Bill_Amount,
SUM(INV.Advance_Amount) as Advance_Amount
from Invoice INV
join Job_Details JD on (JD.Job_ID = INV.Job_ID)
where DATEDIFF(dd, INV.Bill_Submit_Date, GetDate()) > 0
group by JD.Cust_ID, DATEDIFF(dd, INV.Bill_Submit_Date, GetDate())
) as q
join MstCustomer cust on (cust.Cust_ID = q.Cust_ID)
group by cust.Cust_Name
order by cust.Cust_Name;
Test Snippet:
--
-- Sample Data
--
declare #MstCustomer table (Cust_ID int primary key, Cust_Name varchar(30));
declare #Job_Details table (Job_ID int primary key, Cust_ID int);
declare #Invoice table (Invoice_ID int identity(1,1) primary key, Job_ID int, Invoice_No varchar(30), Bill_Amount int, Advance_Amount int, Bill_Date date, Bill_Submit_Date date);
insert into #MstCustomer (Cust_ID, Cust_Name) values (1,'ABC'), (2,'DEF'), (3,'GHI'), (4,'JKL');
insert into #Job_Details (Job_ID, Cust_ID) values (1,1), (2,2), (3,3), (4,4);
insert into #Invoice (Job_ID, Invoice_No, Bill_Amount, Advance_Amount, Bill_Date, Bill_Submit_Date) values
(1,'7/18-19',300,100,GetDate(),GetDate())
,(1,'6/18-19',6000,1000,GetDate(),GetDate()-46)
,(1,'5/18-19',5000,0,GetDate(),GetDate()-46)
,(1,'4/18-19',4000,0,GetDate(),GetDate()-32)
,(1,'3/18-19',3000,0,GetDate(),GetDate()-32)
,(1,'2/18-19',2000,500,GetDate(),GetDate()-1)
,(1,'1/18-19',1000,500,GetDate(),GetDate()-1)
;
--
-- Query
--
select
cust.Cust_Name as "Customer Name",
SUM(CASE WHEN q.Days_Submitted BETWEEN 1 AND 30 THEN q.Bill_Amount END) AS "1-30 Days Amount",
SUM(CASE WHEN q.Days_Submitted BETWEEN 31 AND 45 THEN q.Bill_Amount END) AS "31-45 Days Amount",
SUM(CASE WHEN q.Days_Submitted > 45 THEN q.Bill_Amount END) AS "Above 45 Days Amount",
SUM(q.Bill_Amount - q.Advance_Amount) AS Balance
from
(
select
JD.Cust_ID,
DATEDIFF(dd, INV.Bill_Submit_Date, GetDate()) as Days_Submitted,
SUM(INV.Bill_Amount) as Bill_Amount,
SUM(INV.Advance_Amount) as Advance_Amount
from #Invoice INV
join #Job_Details JD on (JD.Job_ID = INV.Job_ID)
where DATEDIFF(dd, INV.Bill_Submit_Date, GetDate()) > 0
group by JD.Cust_ID, DATEDIFF(dd, INV.Bill_Submit_Date, GetDate())
) as q
join #MstCustomer cust on (cust.Cust_ID = q.Cust_ID)
group by cust.Cust_Name
order by cust.Cust_Name;
Result:
Customer Name 1-30 Days Amount 31-45 Days Amount Above 45 Days Amount Balance
------------- ---------------- ----------------- -------------------- -------
ABC 3000 7000 11000 19000
You query looks like it is 95% of the way there, you just have your sum functions in the wrong place:
select Cust.Cust_Name
,sum(case when DATEDIFF(dd,INV.Bill_Submit_Date,GETDATE()) < 31
then INV.Bill_Amount
else 0
end
) AS [1-30 DAYS]
,sum(case when DATEDIFF(dd,INV.Bill_Submit_Date,GETDATE()) between 31 and 45
then INV.Bill_Amount
else 0
end
) AS [31-45 DAYS]
,sum(case when DATEDIFF(dd,Bill_Submit_Date,GETDATE()) > 45
then INV.Bill_Amount
else 0
end
) AS [ABOVE 45 DAY]
,sum(INV.Bill_Amount) - sum(INV.Advance_Amount) as Balance
from Invoice INV
inner join Job_Details JD
on JD.Job_ID= INV.Job_ID
inner join MstCustomer cust
on cust.Cust_ID= JD.Cust_ID
group by Cust.Cust_Name

SQL Server: Left outer join on whether ID exists

The first three items from table ProviderValueCard add their respective amount if = 1. I'm also trying to add 50 to my TotalScore if the ProviderID exists in table SubscriptionsTV. GroupID is also from SubscriptionsTV in which the condition needs to be met. I beleive I need to have a left outer join in the 2nd query on ProviderID columns from both tables.
DECLARE #ProviderID int = '1717';
WITH cte as(
SELECT TOP 1 ProviderID, Time_Stamp,
SUM(CASE WHEN [AdditionalReports] = '1' THEN 5 ELSE 0 END) as AdditionalReports,
SUM(CASE WHEN [UniqueReportRequests] = '1' THEN 15 ELSE 0 END) as UniqueReportsRequests,
SUM(CASE WHEN [SurveyCompleted] = '1' THEN 30 ELSE 0 END) as SurveyCompleted
--IF #ProviderID EXISTS SUM(THEN 50 ELSE 0 END)
FROM ProviderValueCard
WHERE ProviderID = #ProviderID
GROUP BY Time_Stamp, ProviderID
ORDER BY Time_Stamp DESC
)
SELECT ProviderID, Time_Stamp, (AdditionalReports + UniqueReportsRequests + SurveyCompleted) AS TotalScore
FROM cte
--WHERE GroupID = 2
returns
ProviderID Time_Stamp TotalScore
----------- ----------------------- -----------
1717 2014-08-28 13:03:30.593 45
ProviderValueCard table
ProviderID AdditionalReports UniqueReportRequests SurveyCompleted Time_Stamp
----------- ----------------- -------------------- --------------- -----------------------
1717 0 1 1 2014-08-28 13:03:30.593
SubscriptionsTV table
ProviderID GroupID
----------- -----------
1717 2
My final result is this:
DECLARE #ProviderID int = '1717';
WITH cte as(
SELECT TOP 1 a.ProviderID, Time_Stamp,
SUM(CASE WHEN [AdditionalReports] = '1' THEN 5 ELSE 0 END) as AdditionalReports,
SUM(CASE WHEN [UniqueReportRequests] = '1' THEN 15 ELSE 0 END) as UniqueReportsRequests,
SUM(CASE WHEN [SurveyCompleted] = '1' THEN 30 ELSE 0 END) as SurveyCompleted,
MAX(CASE WHEN b.ProviderID IS NULL THEN 0 ELSE 50 END) as SubscriptionExists
FROM ProviderValueCard a
LEFT JOIN SubscriptionsTV b
ON a.ProviderID = b.ProviderID
WHERE a.ProviderID = #ProviderID AND GroupID = 2
GROUP BY Time_Stamp, a.ProviderID, event
ORDER BY event DESC, Time_Stamp DESC
)
SELECT ProviderID, Time_Stamp, (AdditionalReports + UniqueReportsRequests + SurveyCompleted + SubscriptionExists) AS TotalScore
FROM cte
You are correct that this can be accomplished with a LEFT JOIN, and I'd use MAX() with a CASE statement:
DECLARE #ProviderID int = '1717';
WITH Subs AS (SELECT DISTINCT ProviderID
FROM SubscriptionsTV
)
,cte AS (SELECT TOP 1 a.ProviderID, Time_Stamp,
SUM(CASE WHEN [AdditionalReports] = '1' THEN 5 ELSE 0 END) as AdditionalReports,
SUM(CASE WHEN [UniqueReportRequests] = '1' THEN 15 ELSE 0 END) as UniqueReportsRequests,
SUM(CASE WHEN [SurveyCompleted] = '1' THEN 30 ELSE 0 END) as SurveyCompleted,
MAX(CASE WHEN b.ProviderID IS NULL THEN 0 ELSE 50 END) as SubscriptionExists
FROM ProviderValueCard a
LEFT JOIN Subs b
ON a.ProviderID = b.ProviderID
WHERE a.ProviderID = #ProviderID
GROUP BY Time_Stamp, ProviderID
ORDER BY Time_Stamp DESC
)
SELECT ProviderID, Time_Stamp, (AdditionalReports + UniqueReportsRequests + SurveyCompleted + SubscriptionExists) AS TotalScore
FROM cte
Update: Since multiple providerID's can exist, need DISTINCT, used a 2nd cte above, could also use a correlated sub-select inside the CASE statement.
Wasn't paying attention, ORDER BY is fine in a cte when TOP is used.

Returning percentage according to age intervals from employees table SQL Server

I have a table employees[employee_id,age] and I wish to return the percentage of employees age between 18 and 20, and 26-40, something like:
Age Interval Percent
18-20 35%
26-40 40 %
Thanks
Select t.range as [age interval] , Count(*) as 'number of appereances' from
(Select case when age between 18 and 26 then '18-26'
when age between 26-40 then '26-40' end as range from employees) t
group by t.range
select '18-20',
count(case when age between 18 and 20 then 1 end) * 100.0 / count(*)
from employees
union all
select '26-40',
count(case when age between 26 and 40 then 1 end) * 100.0 / count(*)
from employees
SQL Fiddle Example #1
You could also write a slightly cleaner (easier to maintain) version like this:
select cast(r.Start as varchar(3)) + '-' + cast(r.[End] as varchar(3)),
count(case when e.age between r.Start and r.[End] then 1 end) * 100.0 / (select count(*) from employees)
from (
select 18 as Start, 20 as [End]
union all
select 21 as Start, 25 as [End]
union all
select 26 as Start, 40 as [End]
) r
left outer join employees e on e.age between r.Start and r.[End]
group by cast(r.Start as varchar(3)) + '-' + cast(r.[End] as varchar(3))
SQL Fiddle Example #2
You generally want to do this sort of thing with windows functions:
Select t.range as [age interval] , Count(*) as 'number of appereances',
cast(count(*)*100.0/tot as varchar(256))+'%' as 'percent'
from (Select (case when age between 18 and 26 then '18-26'
when age between 26 and 40 then '26-40'
end) as range,
count(*) over (partition by NULL) as tot
from employees) t
group by t.range
I also formatted the number as you have it in your example.
SELECT
SUM(CASE WHEN [dbo].[GetAge](DateOfBirth) < 20 THEN 1 ELSE 0 END) AS [Under_20],
SUM(CASE WHEN [dbo].[GetAge](DateOfBirth) BETWEEN 20 AND 25 THEN 1 ELSE 0 END) AS [Age_21_25],
SUM(CASE WHEN [dbo].[GetAge](DateOfBirth) >=26 THEN 1 ELSE 0 END) AS [Age_26_Elder]
from AspNetUsers(nolock) au inner join AspNetUserRoles(nolock) aur
on au.Id=aur.UserId inner join AspNetRoles(nolock) ar on aur.RoleId=ar.Id
Inner join StudentProfiles(nolock) st on au.Id = st.UserId
Select
CAST(ROUND(count(case when 18 <= age and age < 26 then 1 end) * 100.0 / count(*),2)AS NUMERIC(8,2)) as '18-26'
,CAST(ROUND(count(case when 26 <= age and age < 40 then 1 end) * 100.0 / count(*),2)AS NUMERIC(8,2)) as '26-40'
From employees
plus obtimisée

Best way to aggregate data - Update query or subquery?

I'm trying to aggregate some customer order data into one table for analysis. The data is the number of products a customer orders, then trying to determine whether the order is a small, medium, or large order, then determine the total products they bought and the cost of the order by OrderSize.
Small order - 1 - 2 products
Medium order - 3 - 4 products
Large Order - >=5 products
Here is the data:
CustomerID OrderID OrderSize OrderTotal
1 800 1 $20
2 801 1 $10
3 802 4 $85
1 803 1 $30
2 804 8 $120
3 805 1 $40
Here is the table I am attempting to build (easier to just post an image):
I could run an update query like this to populate the table:
-- Build the table --
CREATE TABLE Customers (
CustomerID varchar(10) PRIMARY KEY,
SmallOrderCount int,
SmallOrderProducts int,
SmallOrderTotal money,
MedOrderCount int,
MedOrderProducts int,
MedOrderTotal money,
LargeOrderCount int,
LargeOrderProducts int,
LargeOrderTotal money
);
-- Insert the unique customers --
INSERT INTO Customers
SELECT CustomerID FROM Orders GROUP BY CustomerID;
-- Update to populate the order information --
UPDATE Customers
SET SmallOrderCount = (SELECT count(*) FROM Orders WHERE OrderSize BETWEEN 1 AND 2 AND Orders.CustomerID = Customers.CustomerID);
UPDATE Customers
SET SmallOrderProducts = (SELECT sum(OrderSize) FROM Orders WHERE OrderSize BETWEEN 1 AND 2 AND Orders.CustomerID = Customers.CustomerID);
UPDATE Customers
SET SmallOrderTotal = (SELECT sum(OrderTotal) FROM Orders WHERE OrderSize BETWEEN 1 AND 2 AND Orders.CustomerID = Customers.CustomerID);
Then I could repeat this for the remaining 6 columns.
However, this seems like a lot of work. Is there a way to populate my Customer table using a subquery that may be less work? Or is my approach above the most direct?
You can do all of it in one INSERT command with CASE statements:
INSERT INTO Customers
SELECT CustomerID
,sum(CASE WHEN OrderSize BETWEEN 1 AND 2 THEN 1 ELSE 0 END) AS SmallOrderCount
,sum(CASE WHEN OrderSize BETWEEN 1 AND 2 THEN OrderSize ELSE 0 END) AS SmallOrderProducts
,sum(CASE WHEN OrderSize BETWEEN 1 AND 2 THEN OrderTotal ELSE 0 END) AS SmallOrderTotal
,sum(CASE WHEN OrderSize BETWEEN 3 AND 4 THEN 1 ELSE 0 END) AS MedOrderCount
,sum(CASE WHEN OrderSize BETWEEN 3 AND 4 THEN OrderSize ELSE 0 END) AS MedOrderProducts
,sum(CASE WHEN OrderSize BETWEEN 3 AND 4 THEN OrderTotal ELSE 0 END) AS MedOrderTotal
,sum(CASE WHEN OrderSize > 4 THEN 1 ELSE 0 END) AS LargeOrderCount
,sum(CASE WHEN OrderSize > 4 THEN OrderSize ELSE 0 END) AS LargeOrderProducts
,sum(CASE WHEN OrderSize > 4 THEN OrderTotal ELSE 0 END) AS LargeOrderTotal
FROM Orders
GROUP BY CustomerID;
See this working demo for the full query on data.SE.
One insert should be enough:
INSERT INTO Customers(CustomerID,SmallOrderCount,SmallOrderProducts,
SmallOrderTotal)
SELECT a.CustomerID, COUNT(a.*) as cnt, sum(a.OrderSize) as OrderSize,
sum(a.OrderTotal) as OrderTotal
FROM Orders a
WHERE a.OrderSize BETWEEN 1 AND 2
GROUP BY a.CustomerID
The query above will insert only customers whose order size is between 1 and 2. If you need to insert others as well, you can use :
INSERT INTO Customers(CustomerID,SmallOrderCount,SmallOrderProducts,
SmallOrderTotal)
SELECT a.CustomerID,
COUNT(CASE WHEN a.OrderSize BETWEEN 1 AND 2 THEN 1 END) as cnt,
sum(CASE WHEN a.OrderSize BETWEEN 1 AND 2 THEN a.OrderSize ELSE 0 END) as OrderSize,
sum(CASE WHEN a.OrderSize BETWEEN 1 AND 2 THEN a.OrderTotal ELSE 0 END ) as OrderTotal
FROM Orders a
GROUP BY a.CustomerID
I'd only create a single query (which can be used as view) for that and not a whole new persisted table.
WITH cteOrders AS (
SELECT CustomerID,
CASE WHEN OrderSize BETWEEN 1 AND 2 THEN OrderSize END SmallOrderProducts,
CASE WHEN OrderSize BETWEEN 1 AND 2 THEN OrderTotal END SmallOrderTotal,
CASE WHEN OrderSize BETWEEN 3 AND 4 THEN OrderSize END MediumOrderProducts,
CASE WHEN OrderSize BETWEEN 3 AND 4 THEN OrderTotal END MediumOrderTotal,
CASE WHEN OrderSize > 4 THEN OrderSize END LargeOrderProducts,
CASE WHEN OrderSize > 4 THEN OrderTotal END LargeOrderTotal
FROM Orders
)
SELECT CustomerID,
COUNT(SmallOrderProducts) SmallOrderCount,
COALESCE(SUM(SmallOrderProducts), 0) SmallOrderProducts,
COALESCE(SUM(SmallOrderTotal), 0) SmallOrderTotal,
COUNT(MediumOrderProducts) MediumOrderCount,
COALESCE(SUM(MediumOrderProducts), 0) MediumOrderProducts,
COALESCE(SUM(MediumOrderTotal), 0) MediumOrderTotal,
COUNT(LargeOrderProducts) LargeOrderCount,
COALESCE(SUM(LargeOrderProducts), 0) LargeOrderProducts,
COALESCE(SUM(LargeOrderTotal), 0) LargeOrderTotal
FROM cteOrders
GROUP BY CustomerID
You can do this by using single INSERT INTO SELECT statement
INSERT INTO Customers
SELECT * ,
(SELECT count(*) FROM Orders WHERE OrderSize BETWEEN 1 AND 2 AND Orders.CustomerID = CustomerID),
(SELECT sum(OrderSize) FROM Orders WHERE OrderSize BETWEEN 1 AND 2 AND Orders.CustomerID = CustomerID)
FROM CUSTOMERS

Resources