How to calculate Amount by days wise of customer in sql - sql-server

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

Related

Columns created by case statement do not group by

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

Select rolling totals by month more data

I have a query that I have been using to track users sales. Previously they ere required to make a quota each month. Now, however they would like to change the rule to allow them to start any month, so they may go from June to June or whatever. they also want users to start over immediately if they miss a month. This does seem to be a more equitable system because if they didn't make the quota in March, for example, they were unable to count any they made after that month for the entire year. This really messes up my query though, and I don't know how to fix it. anyone have a solution?
here is the existing t-sql.
#Year int
AS
BEGIN
DECLARE #DateStart datetime
DECLARE #DateStop datetime
SELECT #DateStart = CONVERT(DATETIME, CONVERT( char(4), #Year) + '-01- 01')
SELECT #DateStop = CONVERT(DATETIME, CONVERT( char(4), #Year + 1) + '-01- 01')
SET NOCOUNT ON;
SELECT r.riderid,
r.dname,
DATEPART(yyyy, m.ridedate),
SUM(CASE DATEPART(mm, m.datesale) WHEN 1 THEN m.quota ELSE 0 END) AS [jan],
SUM(CASE DATEPART(mm, m.datesale) WHEN 2 THEN m.quota ELSE 0 END) AS [feb],
SUM(CASE DATEPART(mm, m.datesale) WHEN 3 THEN m.quota ELSE 0 END) AS [mar],
SUM(CASE DATEPART(mm, m.datesale) WHEN 4 THEN m.quota ELSE 0 END) AS [apr],
SUM(CASE DATEPART(mm, m.datesale) WHEN 5 THEN m.quota ELSE 0 END) AS [may],
SUM(CASE DATEPART(mm, m.datesale) WHEN 6 THEN m.quota ELSE 0 END) AS [jun],
SUM(CASE DATEPART(mm, m.datesale) WHEN 7 THEN m.quota ELSE 0 END) AS [jul],
SUM(CASE DATEPART(mm, m.datesale) WHEN 8 THEN m.quota ELSE 0 END) AS [aug],
SUM(CASE DATEPART(mm, m.datesale) WHEN 9 THEN m.quota ELSE 0 END) AS [sep],
SUM(CASE DATEPART(mm, m.datesale) WHEN 10 THEN m.quota ELSE 0 END) AS [oct],
SUM(CASE DATEPART(mm, m.datesale) WHEN 11 THEN m.quota ELSE 0 END) AS [nov],
SUM(CASE DATEPART(mm, m.datesale) WHEN 12 THEN m.quota ELSE 0 END) AS [dec],
SUM(m.quota) as [tot]
FROM users u
JOIN mysales m
ON m.riderid = u.riderid
Where m.datesale Between #DateStart AND #DateStop
GROUP BY DATEPART(yyyy, m.datesale), u.userid
ORDER BY DATEPART(yyyy, m.datesale), SUM(m.quota) DESC
END
OK -here is data
The table holds the users id, the customer id , amount of sale and date of sale
The query pulls the user, the sum of sales by month. User 250 made quota in July/2016, but did not in August, so he should get an entry in #quota for July, and September, but because he did not in August he has to restart in September; user# 300 has made quota from Jan 2016 to SEPT so he has qualified for his bonus as long as he finishes the 12 months. User 350 has successfully finished the year and should get a bonus. at this time in prod there is no quota table, would that simplify? What I need is a list of users that are in the running.
--drop table #sales
--drop table #quota
create table #sales
(
--saleid int --PK
userid int -- salesperson FK
, customerid int --FK
, sale_amt decimal
, date_sale datetime
)
insert into #sales values
(300,1301,542.90,'3-2-2016'),
(300,1301,782.70,'3-4-2016'),
(300,1541,600.70,'3-7-2016'),
(300,903,640.71,'3-10-2016'),
(300,1745,900.01,'3-29-2016'),
(300,1440,2040.71,'2-10-2016'),
(300,903,640.71,'2-20-2016'),
(300,414,1489.00,'1-18-2016'),
(300,1645,1322.00,'1-20-2016'),
(300,1200,1156.09,'4-2-2016'),
(300,1204,1456.00,'4-20-2016'),
(250,1140,156.89,'4-12-2016'),
(250,1240,1176.69,'4-14-2016'),
(250,840,480.61,'4-17-2016'),
(250,1940,500.71,'5-17-2016'),
(250,1425,4800.61,'6-1-2016'),
(250,1840,701.32,'6-15-2016'),
(250,1840,701.32,'7-15-2016'),
(250,1840, 2701.32,'8-15-2016'),
(450,8421,2500.61,'7-17-2015'),
(450,8422,2500.1,'8-17-2015'),
(450,843,2500.1,'9-17-2015'),
(450,8431,2500.00,'10-17-2015'),
(450,1431,2500.00,'11-17-2015'),
(450,4311,2500.00,'12-17-2015'),
(450,4310,2500.00,'1-17-2016'),
(450,1310,2500.00,'2-17-2016'),
(450,1310,2500.00,'3-17-2016'),
(450,130,2500.00,'4-17-2016'),
(450,1130,2500.00,'5-17-2016'),
(450,113,2500.00,'6-17-2016')
Select userid
, sum(sale_amt) Sale
, DATEPART(mm,date_sale) as[month]
from #sales
group by userid, DATEPART(mm,date_sale) order by userid
create table #quota
(
qid int --PK
, userid int -- salesperson FK
, quota bit -- awarded when sales => $2500.00
, datesale datetime -- date quota made
)
Just one possible way to write a query that looks back #running_months number of months to verify that no quotas have been missed during the window for each user:
select userid from users u
where not exists (
select 1 from #sales s
where s.userid = u.userid
and date_sale > dateadd(month, -#running_months - 1, current_timestamp)
and datediff(month, sales_date, current_timestamp) between 1 and #running_months
group by month(sales_amt)
having sum(sales_amt) < 2500
)
EDIT: I later realized that you probably do have users with no sales during a month so you'll probably need to actually verify the condition that all the months are over quota rather than none of the months are under quota:
select userid from users u
where userid in (
select userid from
(
select userid from #sales s
where s.userid = u.userid
and date_sale > dateadd(month, -#running_months - 1, current_timestamp)
and datediff(month, sales_date, current_timestamp) between 1 and #running_months
group by month(sales_amt)
having sum(sales_amt) >= 2500
) q
group by userid
having count(*) = #running_months
)

Count of rows for 2 days

My data is in below format:
employee order id date
a 123 01/06/2013
b 124 02/06/2013
a 125 02/06/2013
a 129 02/06/2013
I need the data in below format:
employee day 1 day 2
a 1 2
b 0 1
Try this one -
DECLARE #temp TABLE
(
dtStart DATETIME
, employees CHAR(1)
)
INSERT INTO #temp (employees, dtStart) VALUES('a','01/06/2013')
INSERT INTO #temp (employees, dtStart) VALUES('a','01/06/2013')
INSERT INTO #temp (employees, dtStart) VALUES('b','02/06/2013')
SELECT
employees
, day1 = COUNT(CASE WHEN DAY(dtStart) = 1 THEN 1 END)
, day2 = COUNT(CASE WHEN DAY(dtStart) = 2 THEN 1 END)
FROM #temp
--WHERE dtStart BETWEEN '01/06/2013' AND '30/06/2013'
GROUP BY employees
Something along these lines should work (depending on whether you have a fixed amount of days or not):
select employee,
SUM(CASE WHEN date = '01/06/2013' THEN 1 ELSE 0 END) as day1,
SUM(CASE WHEN date = '02/06/2013' THEN 1 ELSE 0 END) as day2
from table
group by employee
select distinct employees,
SUM(CASE WHEN dtStart = '01/06/2013' THEN 1 ELSE 0 END) as day1,
SUM(CASE WHEN dtStart = '02/06/2013' THEN 1 ELSE 0 END) as day2
from yourTable
group by dtStart,employees
see your demo

create a statistics table using datetime MSSQL Query

I have little table which gives me a very hard time:
Person datetime1 datetime2
Eric 2012-10-01 09:00:05.000 2012-10-01 22:00:00.000
Anna 2012-10-02 06:00:05.000 2012-10-03 12:00:05.000
Richard 2012-10-03 09:00:05.000 2012-10-04 02:00:05.000
Chuck 2012-10-01 12:00:05.000 2012-10-01 23:00:05.000
I am trying to write a query, which gives me statistics table. This table contains information about when a user logged in and out (daily granularity):
Date logged_in logged_off
2012-10-01 2 2
2012-10-02 1 0
2012-10-03 1 1
2012-10-04 0 1
According to my research, a pivot command could solve the problem?
select Person,
SUM(case when datetime1 = '2012-10-01' then 1 else 0 end) as [loggeed_in],
SUM(case when datetime2 = '2012-10-01' then 1 else 0 end) as [logged_of]
from table
group by Person
This is not working... Do you have any ideas?
This will fix the current query, but don't know if it will solve the whole problem...
select Person,
SUM(case when convert(varchar(10), datetime1, 111) = '2012/10/01' then 1 else 0 end) as [loggeed_in],
SUM(case when convert(varchar(10), datetime2, 111) = '2012/10/01' then 1 else 0 end) as [logged_of]
from table
group by Person
EDIT: I believe this will better suit requirements...
SELECT
[Date] = dt,
logged_in = (
SELECT COUNT(*)
FROM table1
WHERE convert(varchar(10), datetime1, 111) = convert(varchar(10), dt, 111)),
logged_off = (
SELECT COUNT(*)
FROM table1
WHERE convert(varchar(10), datetime2, 111) = convert(varchar(10), dt, 111))
FROM (
SELECT TOP 1000
row_number() OVER(ORDER BY (SELECT 0)) AS N
FROM master.dbo.syscolumns sc1, master.dbo.syscolumns sc2) tally
CROSS APPLY(
SELECT dt = DATEADD(dd, tally.N - 1, '2012-10-1')) tallydt
WHERE dt BETWEEN (SELECT MIN(dateadd(dd, -1, datetime1)) FROM table1) AND (SELECT MAX(datetime2) FROM table1)
GROUP BY dt
ORDER BY dt
Here is the working solution:
WITH O AS (
SELECT
CAST([login Date & Time] AS DATE) loginDate
,COUNT(*) logined
FROM table
GROUP BY CAST([login Date & Time] AS DATE)
), C AS (
SELECT
CAST([Close Date & Time] AS DATE) CloseDate
,COUNT(*) Closed
FROM table
WHERE [Close Date & Time] IS NOT NULL
GROUP BY CAST([Close Date & Time] AS DATE)
)
SELECT
COALESCE(C.CloseDate, O.loginDate) TheDate
--,O.loginDate
--,C.CloseDate
,O.logined
,C.Closed
FROM O
FULL JOIN C
ON O.loginDate = C.CloseDate
ORDER BY TheDate

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

Resources