How to calling special condition in some column in SQL Server - sql

For this query :
WITH CTE (customerID,FirstWeek,RN) AS
(
SELECT
customerID, MIN(DATEPART(week, tp_date)) TransWeek,
ROW_NUMBER() OVER (PARTITION BY customerID ORDER BY DATEPART(week, tp_date) ASC)
FROM
all_table
GROUP BY
customerID, DATEPART(week, tp_date)
)
SELECT
CTE.customerID, CTE.FirstWeek,
(SELECT TOP 1 (DATEPART(week, c.tp_date))
FROM all_table c
WHERE c.customerID = CTE.customerID
AND DATEPART(week, C.tp_date) > CTE.FirstWeek) SecondWeek
FROM
CTE
WHERE
RN = 1
I get this result :
CustomerID
firstweek
secondweek
C001
35
37
C002
35
37
C003
35
39
C003
36
37
But what to do if I want the result to show only the first week in 35 and second week 37? The result should look like this:
CustomerID
firstweek
secondweek
C001
35
37
C002
35
37

Simply, run a second CTE to apply your filter in final query. Also, you may not need the ROW_NUMBER window function:
WITH agg AS (
SELECT
customerID,
MIN(DATEPART(week, tp_date)) AS FirstWeek
FROM all_table
GROUP BY
customerID
), main AS (
SELECT
customerID,
FirstWeek,
(
SELECT TOP 1 DATEPART(week, c.tp_date)
FROM all_table c
WHERE c.customerID = agg.customerID
AND DATEPART(week, c.tp_date) > agg.FirstWeek
) AS SecondWeek
FROM agg
)
SELECT *
FROM main
WHERE FirstWeek = 35
AND SecondWeek = 37

Related

Calculate and find the second day of the week in SQL

Let's say I have data like this
CustomerID
Trans_date
C001
01-sep-22
C001
04-sep-22
C001
14-sep-22
C002
03-sep-22
C002
01-sep-22
C002
18-sep-22
C002
20-sep-22
C003
02-sep-22
C003
28-sep-22
C004
08-sep-22
C004
18-sep-22
But I'm unable to find the first and second transaction based on Trans_date.
I wish for the result to look like this:
CustomerID
Trans_week
first
second
C001
35
35
37
C001
35
35
37
C001
37
35
37
C002
35
35
37
C002
35
35
37
C002
37
35
37
C002
38
35
37
C003
35
35
39
C003
39
35
39
C004
36
36
37
C004
37
36
37
And for the last result will show like this:
CustomerID
first
second
C001
35
37
C002
35
37
C003
35
39
C004 didnt include because i would need who cust id who come first in their 1st week.
You may use ROW_NUMBER() function -inside a subquery- to get the first and second transaction dates for a customer, then use conditional MAX window function on the results of that subquery.
SELECT CustomerID, DATEPART(week,CustTrans) AS Trans_week,
DATEPART(week, MAX(CASE rn WHEN 1 THEN CustTrans END) OVER (PARTITION BY CustomerID)) first,
DATEPART(week, MAX(CASE rn WHEN 2 THEN CustTrans END) OVER (PARTITION BY CustomerID)) second
FROM
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY CustTrans) rn
FROM trydata
) T
ORDER BY CustomerID, Trans_week
See a demo on SQL Server.
As you requested in the comments, if you want to select only one row per customer that showing the first and second weeks, use the following query:
SELECT CustomerID,
DATEPART(week, MAX(CASE rn WHEN 1 THEN CustTrans END)) first,
DATEPART(week, MAX(CASE rn WHEN 2 THEN CustTrans END)) second
FROM
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY CustTrans) rn
FROM trydata
) T
WHERE rn <= 2
GROUP BY CustomerID
ORDER BY CustomerID
See a demo.
with cte (RN,CustomerID, FirstWeek,SecondWeek ) as
( SELECT ROW_NUMBER() over(partition by CustomerID ORDER BY CustomerID ) RN, CustomerID,FirstWeek, isnull((select TOP 1 (DATEPART(week,CustTrans))
from trydata c
where c.CustomerID = SRC.CustomerID AND DATEPART(week,C.CustTrans) > SRC.FirstWeek
ORDER BY DATEPART(week,C.CustTrans) ),'0') AS SecondWeek
FROM (
SELECT CustomerID,DATEPART(week,CustTrans) TransWeek,
(select MIN(DATEPART(week,CustTrans)) from trydata c where c.CustomerID = trydata.CustomerID) AS FirstWeek
FROM trydata
) SRC )
select CustomerID,FirstWeek,SecondWeek from cte where RN = 1
Output:
Example 2 :
WITH CTE (CustomerID,FIrstWeek,RN) AS (
SELECT CustomerID,MIN(DATEPART(week,CustTrans)) TransWeek,
ROW_NUMBER() over(partition by CustomerID ORDER BY DATEPART(week,CustTrans) asc ) FROM TryData
GROUP BY CustomerID,DATEPART(week,CustTrans)
)
SELECT CTE.CustomerID, CTE.FIrstWeek,
(select TOP 1 (DATEPART(week,c.CustTrans))
from trydata c
where c.CustomerID = CTE.CustomerID AND DATEPART(week,C.CustTrans) > CTE.FIrstWeek
) SecondWeek
FROM CTE
WHERE RN = 1
FIddle Demo
Edit: This can be done on easier way and less complex.

SQL filling missing date entries, and including previous date's counts

I have a table as follows
Date
Id
Group
Name
ScoreCount
2022-06-20
1
Athlete
Adam
52
2022-06-23
1
Athlete
Adam
77
2022-06-25
1
Athlete
Adam
79
2022-06-19
1
Employee
Adam
65
2022-06-22
1
Employee
Adam
28
I'd like this for the dates to be added for each individual id and type of group. So it should look something like:
Date
Id
Group
Name
ScoreCount
2022-06-20
1
Athlete
Adam
52
2022-06-21
1
Athlete
Adam
52
2022-06-22
1
Athlete
Adam
52
2022-06-23
1
Athlete
Adam
77
2022-06-24
1
Athlete
Adam
77
2022-06-25
1
Athlete
Adam
79
2022-06-19
1
Employee
Adam
65
2022-06-20
1
Employee
Adam
65
2022-06-21
1
Employee
Adam
65
2022-06-22
1
Employee
Adam
28
My code is as follows:
WITH t as (SELECT
Id,
Group,
Name,
min(Date) as MinDate
max(Date) as MaxDate
FROM recordTable
GROUP BY Id,Group,Name
SELECT t.Id,
t.Group,
t.Name,
c.Days,
(SELECT LAST_VALUE(ScoreCount) FROM recordTable WHERE t.Id = recordTable.Id AND t.Group = recordTable.Group)
FROM t
LEFT JOIN calendar c ON c.Days BETWEEN t.MinDate AND t.MaxDate
calendar is the table that contains individual dates for the year 2022, so they can be joined. Everything works, except for the ScoreCount, which Last_Value isn't actually doing what I want it to do. How can I fix this?
You can simply try reversing the order of your joined tables -
WITH t as (SELECT Id,
Group,
Name,
min(Date) as MinDate,
max(Date) as MaxDate
FROM recordTable
GROUP BY Id,Group,Name
)
SELECT t.Id,
t.Group,
t.Name,
c.Days,
(SELECT LAST_VALUE(ScoreCount) OVER(<your over clause is missing>)
FROM recordTable
WHERE t.Id = recordTable.Id
AND t.Group = recordTable.Group)
FROM calendar c
LEFT JOIN t ON c.Days BETWEEN t.MinDate AND t.MaxDate
Although I have not tested the query yet this will give you an idea to proceed further.
You don't need the last_value, you can get the first value
WITH t as (
SELECT
[Id],
[Group],
[Name],
min([Date]) as MinDate,
max([Date]) as MaxDate
FROM recordTable
GROUP BY [Id],[Group],[Name]
)
SELECT
t.Id,
t.[Group],
t.[Name],
c.[Date],
(SELECT top 1 ScoreCount
from recordTable x
where x.[Date] <= c.[Days]
and x.[Group] = t.[Group]
and x.[Name] = t.[Name]
order by x.[Date] desc
) ScoreCount
FROM t
LEFT JOIN calendar c ON c.[Days] BETWEEN t.MinDate AND t.MaxDate

cumulative count returning more rows than expected

base_table
eom account_id closings checkouts
2018-11-01 1 21 147
2018-12-01 1 20 214
calendar_table
month account_id
2020-11-01 1
2014-04-01 1
Based on two tables, above, I would like to create a month-by-month cumulative closings and checkouts.
The calendar_table contains the months the account id is active. Thus, it is used as the main table (in the from clause).
with
base_table as (
select eom, account_id, closings, checkouts
from base_table bt
where account_id in (3,30,122,152,161,179)
)
,calendar_table as (
select ct.month, c.external_id as account_id
from calendar_table ct
left join customers c
on c.id = ct.organization_id
where account_id in (3,30,122,152,161,179)
)
,cumulative_table as (
select ct."month"
,list.account_id
,coalesce(bt.closings,0) as closings
,coalesce(sum(closings) OVER (PARTITION BY list.account_id
ORDER BY ct."month"
rows BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),0)
as cum_closings
,coalesce(bt.checkouts,0) as checkouts
,coalesce(sum(checkouts) OVER (PARTITION BY list.account_id
ORDER BY ct."month"
rows BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),0)
AS cum_checkouts
from calendar_table ct
cross join (select distinct account_id from base_table) list
left join base_table bt
on bt.account_id = list.account_id and bt.eom = ct.month
)
select *
from cumulative_table
The query above returns a cumulative table that contains duplications, probably because of the cross join.
month account_id closings cum_closings checkouts cum_checkouts
01/11/17 1 20 20 282 282
01/11/17 1 20 40 282 564
01/11/17 1 20 60 282 846
01/12/17 1 17 77 346 1192
01/12/17 1 17 94 346 1538
01/12/17 1 17 111 346 1884
I expect the query to return one month per account id.
month account_id closings cum_closings checkouts cum_checkouts
01/11/17 1 20 20 282 282
01/12/17 1 17 37 346 628
You can do more simple :
WITH list AS
( select bt.eom, bt.account_id, bt.closings, bt.checkouts
, ct.month, ct.organization_id
from base_table bt
inner join calendar_table ct
on ct.account_id = bt.account_id
where bt.account_id in (3,30,122,152,161,179)
)
select l.month, c.external_id as account_id
, coalesce(l.closings,0) as closings
, coalesce( sum(l.closings) OVER (PARTITION BY l.account_id
ORDER BY l."month"
rows BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
, 0
) as cum_closings
, coalesce(l.checkouts,0) as checkouts
, coalesce( sum(l.checkouts) OVER (PARTITION BY l.account_id
ORDER BY l."month"
rows BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
, 0
) AS cum_checkouts
from list as l
left join customers c
on c.id = l.organization_id ;

SQL query group by with null values is returning duplicates

I have following query
My #dates table has following records:
month year saledate
9 2020 2020-09-01
10 2020 2020-10-01
11 2020 2020-11-01
with monthlysalesdata as(
select month(salesdate) as salemonth, year(salesdate) as saleyear,salesrepid, salespercentage
from salesrecords r
join #dates d on d.saledate = r.salesdate
group by salesrepid, salesdate),
averagefor3months as(
select 0 as salemonth, 0 as saleyear, salesrepid, salespercentage
from monthlysalesdata
group by salesrepid)
finallist as(
select * from monthlysalesdata
union
select * from averagefor3months
This query returns following records which gives duplicate for a averagefor3months result set when there is null record in the first monthlyresultdata. how to achieve average for 3 months as one record instead of having duplicates?
salesrepid salemonth saleyear percentage
232 0 0 null -------------this is the duplicate record
232 0 0 90
232 9 2020 80
232 10 2020 null
232 11 2020 100
My first cte has this result:
salerepid month year percentage
---------------------------------------------
232 9 2020 80
232 10 2020 null
232 11 2020 100
My second cte has this result:
salerepid month year percentage
---------------------------------------------
232 0 0 null
232 0 0 90
How to avoid the duplicate record in my second cte,
I suspect that you want a summary row per sales rep based on some aggregation. Your question is not clear on what is needed for the aggregation, but something like this:
with ym as (
select r.salesrepid, d.year, d.month, sum(<something>) as whatever
from salesrecords r join
#dates d
on d.saledate = r.salesdate
group by r.salesrepid, d.year, d.month
)
select ym.*
from ym
union all
select salesrepid, null, null, avg(whatever)
from hm
group by salesrepid;
I updated to selected the group by from the table directly instead of the previous cte and got my results. Thank you all for helping
with ym as (
select r.salesrepid, d.year, d.month, sum(<something>) as whatever
from salesrecords r join
#dates d
on d.saledate = r.salesdate
group by r.salesrepid, d.year, d.month
),
threemonthsaverage as(
select r.salesrepid, r.year, r.month, sum(something) as whatever
from salesrecords as r
group by salesrepid)
select ym *
union
select threemonthsaverage*

Select Highest value against each record in SQL

I am new to SQL and the problem I am having is that I have the value for alot assets in a table.
I need to get the highest speed for each asset in that table.
I have tried searching google but I found the MAX() function of SQL.
I don't need the MAX() because that will only give me one record with the highest value. I need the highest for each asset:
e.g.
iAssetId fSpeedKPH
1 78
5 77
5 80
8 74
8 81
8 88
8 111
24 71
24 78
24 79
24 79
24 82
24 84
24 90
24 91
24 92
I have highlighted the highest row for each asset i.e. AssetId = 1, 5, 24 and 8
These are the rows I need to select.
What is the most efficient way?
Do I have to loop through this result-set returned by the SQL I have written?
EDIT:
My SQL:
DECLARE #dateMinusDay datetime = DateAdd(dd, -1, GetDate())
select vm.iAssetId, max(vm.fSpeedKPH), vm.dtUTCDateTime, ge.sGeofenceName from VehicleMonitoringLog vm
inner join Geofences ge on ge.iGeofenceId = vm.iGeofenceId
where vm.iGeofenceId != 1 AND vm.fSpeedKPH > 70 AND (vm.dtUTCDateTime > #dateMinusDay AND vm.dtUTCDateTime < GETDATE())
group by
vm.iAssetId,vm.fSpeedKPH, vm.dtUTCDateTime, ge.sGeofenceName
select iAssetId, max(fSpeedKPH)
from AssetsTable
group by iAssetId
SELECT iAssetId, fSpeedKPH
FROM (
SELECT iAssetId, fSpeedKPH
,ROW_NUMBER() OVER (PARTITION BY iAssetId ORDER BY fSpeedKPH DESC) AS RN
FROM Table_Name )Sub
WHERE RN = 1
UPDATE
DECLARE #dateMinusDay datetime = DateAdd(dd, -1, GetDate())
SELECT Q.iAssetId, Q.dtUTCDateTime, Q.sGeofenceName
FROM (
select vm.iAssetId
, vm.dtUTCDateTime
, ge.sGeofenceName
,ROW_NUMBER() OVER (PARTITION BY vm.iAssetId ORDER BY vm.fSpeedKPH DESC) AS RN
from VehicleMonitoringLog vm inner join Geofences ge
on ge.iGeofenceId = vm.iGeofenceId
where vm.iGeofenceId != 1 AND vm.fSpeedKPH > 70
AND (vm.dtUTCDateTime > #dateMinusDay --<-- Instead of variable you can use GETDATE() - 1
AND vm.dtUTCDateTime < GETDATE())
)Q
WHERE RN = 1