select rows with events related with another events in the same query column - sql

I need to select rows with EventTypeID = 19 which does not have related EventtypeID = 21 LoggedOn exactly 4 minutes earlier for the same EmployeeID. Here's the query bellow and some raw output:
SELECT * FROM
(
SELECT rcp..EventLogEntries.EmployeeID, rcp..EventLogEntries.EventTypeID, rcp..EventLogEntries.TerminalID, rcp..EventLogEntries.LoggedOn
FROM rcp..EventLogEntries
WHERE rcp..EventLogEntries.terminalid = 3
UNION
SELECT viso..AccessUserPersons.UserExternalIdentifier, rcp..EventTypes.ID, rcp..Terminals.ID, viso..EventLogEntries.LoggedOn
FROM viso..EventLogEntries, viso..AccessUserPersons, rcp..Terminals, rcp..EventTypes
WHERE viso..EventLogEntries.LocationID = 10
AND viso..EventLogEntries.EventCode = 615
AND rcp..EventTypes.Code = 36
AND viso..EventLogEntries.PersonID = viso..AccessUserPersons.ID
AND viso..EventLogEntries.locationID = rcp..Terminals.TerminalTAID
) results
ORDER BY LoggedOn
EmployeeID EventTypeID TerminalID LoggedOn
273 19 3 2018-12-04 12:31:23.000
273 21 3 2018-12-04 12:34:18.000
483 19 3 2018-12-04 12:40:10.000
268 19 3 2018-12-04 13:19:23.000
273 21 3 2018-12-04 13:28:00.000
273 19 3 2018-12-04 13:32:00.000
459 19 3 2018-12-04 15:01:04.000
What I need to achieve is:
EmployeeID EventTypeID TerminalID LoggedOn
273 19 3 2018-12-04 12:31:23.000
483 19 3 2018-12-04 12:30:10.000
268 19 3 2018-12-04 13:19:23.000
459 19 3 2018-12-04 15:01:04.000
TerminalID column value is always 3 in that scenario and it's not related with any query condition, but must be in the output for syntax requirement in the futher processing.

The bad practice to join tables using conditions in WHERE. The block WHERE need to use to filter first of all.
And aliases help to make code shorter.
SELECT * FROM
(
SELECT EmployeeID, EventTypeID, TerminalID, LoggedOn
FROM rcp..EventLogEntries
WHERE terminalid = 3
UNION
SELECT p.UserExternalIdentifier, et.ID, t.ID, el.LoggedOn
FROM viso..EventLogEntries el
JOIN viso..AccessUserPersons p ON el.PersonID = p.ID
JOIN rcp..Terminals t ON el.locationID = t.TerminalTAID
JOIN rcp..EventTypes et ON --!!! no any condition here
WHERE el.LocationID = 10
AND el.EventCode = 615
AND et.Code = 36
) results
ORDER BY LoggedOn
Try to use the following:
WITH cteData AS
(
SELECT EmployeeID, EventTypeID, TerminalID, LoggedOn
FROM rcp..EventLogEntries
WHERE terminalid = 3
UNION
SELECT p.UserExternalIdentifier, et.ID, t.ID, el.LoggedOn
FROM viso..EventLogEntries el
JOIN viso..AccessUserPersons p ON el.PersonID = p.ID
JOIN rcp..Terminals t ON el.locationID = t.TerminalTAID
JOIN rcp..EventTypes et ON --!!! no any condition here
WHERE el.LocationID = 10
AND el.EventCode = 615
AND et.Code = 36
)
SELECT q19.*
FROM
(
SELECT *
FROM cteData
WHERE EventTypeID=19
) q19
LEFT JOIN
(
SELECT *
FROM cteData
WHERE EventTypeID=21
) q21
ON q19.EmployeeID=q21.EmployeeID
WHERE (DATEDIFF(MINUTE,q19.LoggedOn,q21.LoggedOn)>4 OR q21.LoggedOn IS NULL)
If there don't need any conditions you can use CROSS JOIN.
I got your result using your data:
WITH cteData AS(
SELECT *
FROM (VALUES
(273,19,3,CAST('2018-12-04 12:31:23.000' AS datetime)),
(273,21,3,CAST('2018-12-04 12:34:18.000' AS datetime)),
(483,19,3,CAST('2018-12-04 12:40:10.000' AS datetime)),
(268,19,3,CAST('2018-12-04 13:19:23.000' AS datetime)),
(273,21,3,CAST('2018-12-04 13:28:00.000' AS datetime)),
(273,19,3,CAST('2018-12-04 13:32:00.000' AS datetime)),
(459,19,3,CAST('2018-12-04 15:01:04.000' AS datetime))
)v(EmployeeID,EventTypeID,TerminalID,LoggedOn)
)
SELECT q19.*
FROM
(
SELECT *
FROM cteData
WHERE EventTypeID=19
) q19
LEFT JOIN
(
SELECT *
FROM cteData
WHERE EventTypeID=21
) q21
ON q19.EmployeeID=q21.EmployeeID
WHERE (DATEDIFF(MINUTE,q19.LoggedOn,q21.LoggedOn)>4 OR q21.LoggedOn IS NULL)

Small change done in this part:
ON q19.EmployeeID=q21.EmployeeID AND q19.LoggedOn=dateadd(mi, 4, q21.LoggedOn)
WHERE q21.LoggedOn IS NULL

Related

SQL Server - SUM and comma-separated values using GROUP BY clause

I have 2 tables:
NDEvent:
EventId EndTime
33 2020-10-23 15:00:00.000
33 2020-10-23 15:00:00.000
35 2020-10-21 03:30:00.000
35 2020-10-24 15:00:00.000
35 2020-10-25 15:00:00.000
34 2020-10-23 15:00:00.000
EventAppointment:
Id DocId EventId Amount
1 7647 34 10.00
2 7647 34 10.00
3 28531 33 20.00
4 7647 35 20.00
5 7647 35 100.00
6 7647 35 200.00
And I want result to be like this:
DocId EventId Amount Id
7647 34 20.00 1,2
28531 33 20.00 3
7647 35 320.00 4,5,6
What I have tried is:
select e.Amount,e.DoctorId,e.EventId,
Id= STUFF(
(SELECT DISTINCT ',' + CAST(e.Id as nvarchar(max))
from NDEvent nd
inner join EventAppointment e on nd.Id = e.EventId
where
GETDATE() > nd.EndTime
GROUP BY
e.Amount,e.DoctorId,e.EventId,e.Id
FOR XML PATH(''))
, 1, 1, ''
)
from NDEvent nd
inner join EventAppointment e on nd.Id = e.EventId
where
GETDATE() > nd.EndTime
GROUP BY
e.Amount,e.DoctorId,e.EventId
But it is not giving expected result.
Could anyone help with this query? Or point me to a right direction? Thank you.
It doesn't look like yo need to NDEvent table here at all (though I include it in the sample data). Just SUM and STRING_AGG against EventAppointment:
USE Sandbox
GO
WITH NDEvent AS(
SELECT *
FROM (VALUES(33,CONVERT(datetime,'2020-10-23T15:00:00.000')),
(33,CONVERT(datetime,'2020-10-23T15:00:00.000')),
(35,CONVERT(datetime,'2020-10-21T03:30:00.000')),
(35,CONVERT(datetime,'2020-10-24T15:00:00.000')),
(35,CONVERT(datetime,'2020-10-25T15:00:00.000')),
(34,CONVERT(datetime,'2020-10-23T15:00:00.000')))V(EventID,EndTime)),
EventAppointment AS(
SELECT *
FROM (VALUES(1,7647 ,34,10.00),
(2,7647 ,34,10.00),
(3,28531,33,20.00),
(4,7647 ,35,20.00),
(5,7647 ,35,100.00),
(6,7647 ,35,200.00))V(Id,DocId, EventID, Amount))
SELECT DocID,
EventID,
SUM(Amount) AS Amount,
STRING_AGG(Id,',') WITHIN GROUP (ORDER BY Id) AS IDs
FROM EventAppointment EA
GROUP BY DocId,
EventID;
Can be used in other data.
WITH Table1 AS(
SELECT EventId FROM NDEvent
GROUP BY EventId
),
Table2 AS(
SELECT e.DocId,e.EventId,e.Amount,
STUFF((
SELECT ',' + CAST(ee.Id as nvarchar)
FROM EventAppointment ee
where ee.EventId = e.EventId
GROUP BY ee.EventId,ee.Id
FOR XML PATH('')), 1, 1, '') AS Id
FROM Table1 t
LEFT OUTER JOIN EventAppointment e ON t.EventId = e.EventId
)
SELECT DocId,EventId,SUM(Amount) AS Amount,Id FROM Table2
GROUP BY DocId,EventId,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*

SQL: selecting rows where column value changed last time

I need to get the latest date where number is changed I have this SQL statement
Select
a.group, a.date a.number
From
xx.dbo.list a
Where
a.group in ('10, '10NC', '210')
And a.date >= '2018-06-01'
And a.number > 0
And a. number <> (Select Top 1 b.number
From xxx.dbo.list b
Where b.group = a.group
And b.date >= '2018-06-01'
And b.number > 0
And b.date < a.date
Order by b.date desc)
order by a.date desc
I have a table that looks like this
Group date Number
--------------------------
10 2018-02-06 4
10 2018-04-06 4
10 2018-06-12 4
10NC 2018-02-06 68
10NC 2018-04-06 35
10NC 2018-06-11 35
10NC 2018-06-12 68
10NC 2018-06-13 35
210 2018-06-02 94
210 2018-06-04 100
210 2018-06-06 100
210 2018-06-07 93
I get this output now, but I only want to get the rows with X
Group date Number
------------------------------
10NC 2018-06-12 68
10NC 2018-06-13 35 X
210 2018-06-04 100
210 2018-06-07 93 X
Can anyone help?
You would use lag():
select a.*
from (select a.group, a.date, a.number, lag(a.number) over (partition by group order by date) as prev_number
From xx.dbo.list a
where a.group in ('10', '10NC', '210') And
a.date >= '2018-06-01' And
a.number > 0
) a
where prev_number <> number;
Is this what is Expected?
DECLARE #List TABLE ([Group] VARCHAR(100), [Date] DATE, Number INT)
INSERT INTO #List
SELECT '10','2018-02-06',4
UNION ALL
SELECT '10','2018-04-06',4
UNION ALL
SELECT '10','2018-06-12',4
UNION ALL
SELECT '10NC','2018-02-06',68
UNION ALL
SELECT '10NC','2018-04-06',35
UNION ALL
SELECT '10NC','2018-06-11',35
UNION ALL
SELECT '10NC','2018-06-12',68
UNION ALL
SELECT '10NC','2018-06-13',35
UNION ALL
SELECT '210','2018-06-02',94
UNION ALL
SELECT '210','2018-06-04',100
UNION ALL
SELECT '210','2018-06-06',100
UNION ALL
SELECT '210','2018-06-07',93
;WITH CTE AS
(
SELECT
*
,RN = ROW_NUMBER() OVER (Partition by [Group] ORDER BY [DATE] DESC)
FROM #List
WHERE
[Date] >= '2018-06-01'
AND [Group] in ('10', '10NC', '210')
And Number > 0
)
SELECT * FROM CTE WHERE RN = 1
Note: I am posting it directly in answer as i don't have enough reputation to ask questions in comments.

Cumulative result of items for drawing

I have a table ENG_Drawing.Its fields are
SELECT DrawingID,DrawingNumber,DrawingDescription FROM ENG_Drawing
DrawingID DrawingNumber DrawingDescription etc..
1131 R152-14-01-00-31 DU - 01 etc..
....
1200 R160-02-01-00-21 DU - 12 etc..
....
.....
....
1500 R156-01-01-00-11 DU - 00 etc..
....
.....
2993 R167-09-01-00-21 DU - 04 etc..
2994 R165-10-01-00-40 DU - 25 etc..
2995 R163-13-01-00-01 DU - 24 etc..
2996 R100-12-01-00-61 DU - 21 etc..
2997 R166-09-01-00-21 DU - 21 etc..
2998 R125-14-01-00-02 DU - 32 etc..
2999
3000
.
.
And detail table for drawing table is
DrawingID SeqNbr Drawing_f BOMQuantity ItemQty DrawingreferenceID ItemID
1131 7 0 2 0.574 1231
1131 9 1 4 null 1200 null
....
....
1200 14 1 8 0.560 1100 1555
1200 16 1 2 5.23 1500 1755
....
....
1500 18 0 2 4.350 2684
....
2993 5 0 2 0.061 2125
2993 6 0 2 1.592 1698
2993 7 0 2 2.500 1231
2993 9 1 4 null 1131 null
2993 16 1 2 null 1500 null
2993 18 0 2 4.350 2684
When I give DrawingID as 2993
In need output like this
ITEMID TOTALQTY
1231 9.592 //select((2*2.500) + (4*2*.574)) here (2*2.500)- is BOMQuantity(4)*ItemQty(2.500) for ItemID 2993 and (4*2*.574)- BOMQuantity of DrawingID 1131(4)*BOMQuantity of ItemID 1231 in DRawingID 1131(2)*ItemQty(.574)
1555 71.680 //select(4*4*8*.560) here 4-BOMQuantity of DrawingreferenceID 1131 in DrawingID 2993.Middle 4 is BOMQuantity of DrawingreferenceID 1200 in DrawingID 1131. 8 is BOMQuantity of ItemId 1555 in DrawingId 1200
1698 3.184 //select(2*1.592)
1755 50.944 //select(4*4*2*1.592)
2125 0.122 //select(2*0.061)
2684 0.122 //select((2*4.350)+(2*2*4.350))
ENG_Drawing_BOM contains subdrawings (DrawingreferenceID).they(DrawingreferenceID) have their values in ENG_Drawing table and also have items and subdrawings.
One drawing may have no of subdrawings and that subdrawings can have subdrawings and so on....
I need When i give DrawingID i need Item wise cumulation.The result should be is ITEMID,sum( BOMQuantity * ItemQty) as TOTALQTY.
The problem is,
For exmple
DrawingID has subdrawing with 4 BOMQuantity, i need TOTALQTY 4 times of that subdrawing(S1) items,
if that subdrawing has subdrawings(S2) with 6 BOMQuantity need 4 times of S1 items and 4*6 4 times of S2 items ...It will continue for its inner levels...
I finally need Overall ITEMID wise cumulation
here 2993 as main drawing.1131,1200,1500 are subdrawings and that subdrawings may have sub drawings.I need them also .
Any body????
#Suresh recheck your sample data,what is wrong in my query ?
Declare #drawing table( DrawingID int,SeqNbr int,Drawing_f int,BOMQuantity decimal(5,2),ItemQty decimal(6,3),DrawingreferenceID int,ItemID int)
insert into #drawing
select 1131 , 7, 0,2, 0.574,null,1231 union all
select 1131 , 9, 1,4,null ,1200,null union all
select 1200, 14, 1,8,0.560, 1100,1555 union all
select 1200, 16, 1,2,5.23 , 1500,1755 union all
select 1500, 18, 0,2,4.350, null,2684 union all
select 2993, 5, 0,2,0.061, null,2125 union all
select 2993, 6, 0,2,1.592, null,1698 union all
select 2993, 7, 0,2,2.500, null,1231 union all
select 2993, 9, 1,4,null , 1131,null union all
select 2993, 16, 1,2,null , 1500,null union all
select 2993, 18, 0 ,2,4.350, null,2684
--select * from #drawing
;with cte as
(select * ,1 roots from #drawing where DrawingID=2993
union all
select d.*,case when d.DrawingID=c.DrawingreferenceID then roots+1 else roots end from #drawing d inner join cte c on d.DrawingID=c.DrawingreferenceID
)
select * from cte
#Suresh if your last query is ok then make it accurate
with cte as
(
select d.* , 1 as Nooff,1 roots,d.DrawingID as 'MainDrawing' from ENG_Drawing_BOM d
union all
select d.*,c.BOMQuantity*C.Nooff as Nooff,case when d.DrawingID=c.DrawingreferenceID then roots+1 else roots end ,MainDrawing as 'MainDrawing'
from ENG_Drawing_BOM d inner join cte c on d.DrawingID=c.DrawingreferenceID
)
,CTE1 as
(
select
cte.MainDrawing,
cte.ItemID,
SUM(Nooff*cte.UnitWeight)as 'Wt',
SUM((TotalQty)*Nooff) as 'TotalQty'
from cte
where DrawingReferenceID is null AND MainDrawing = 2993
group by cte.ItemID, cte.MainDrawing
)
select
cte.MainDrawing,
cte.ItemID,
i.ItemDescription,
Wt,
TotalQty
from cte1
inner join STR_Item i on cte.ItemID = i.ItemID
where DrawingReferenceID is null AND MainDrawing = 2993
ORDER BY cte.ItemID
This is exact need ...derived from KumarHarsh reply.
with cte as
(
select d.* , 1 as Nooff,1 roots,d.DrawingID as 'MainDrawing' from ENG_Drawing_BOM d
union all
select d.*,c.BOMQuantity*C.Nooff as Nooff,case when d.DrawingID=c.DrawingreferenceID then roots+1 else roots end ,MainDrawing as 'MainDrawing' from ENG_Drawing_BOM d inner join cte c on d.DrawingID=c.DrawingreferenceID
)
select
cte.MainDrawing,
cte.ItemID,
i.ItemDescription,
SUM(Nooff*cte.UnitWeight)as 'Wt',
SUM((TotalQty)*Nooff) as 'TotalQty'
from cte
inner join STR_Item i on cte.ItemID = i.ItemID
where DrawingReferenceID is null AND MainDrawing = 2993
group by cte.ItemID,
cte.MainDrawing,
i.ItemDescription
ORDER BY cte.ItemID

Selecting duplicates based on some criteria

I have a table which records the activation and deactivation of a module by users. I would like to select only those rows in my table that have maximum time differences between activation and deactivation.
user activation Deactivation usage dmb
Pejman 2005-10-04 14:02:00 2012-09-28 18:29:00 23 198
Pejman 2006-05-30 12:44:00 2012-09-28 18:29:00 34 198
Pejman 2009-11-18 11:06:00 2012-09-28 18:29:00 64 198
Shahin 2005-02-11 10:53:00 2012-09-28 18:29:00 52 323
Shahin 2012-06-24 08:35:00 2012-09-28 18:29:00 17 323
Shahin 2005-02-24 14:22:00 2006-01-16 09:03:00 19 323
Kazem 2008-01-21 22:32:00 2011-01-01 00:00:00 73 666
Kazem 2008-01-21 22:35:00 2012-09-28 18:29:00 62 666
my desired output
user
Pejman 2005-10-04 14:02:00 2012-09-28 18:29:00 23 198
Shahin 2005-02-11 10:53:00 2012-09-28 18:29:00 52 323
Kazem 2008-01-21 22:35:00 2012-09-28 18:29:00 62 666
to find the duration (difference between activation and deactivation) I am using the following sql code (MSSQL)
datediff(d,isnull(deactivation, getdate()), activation) AS Time_Difference
edit
My table is a result of "with" operation. Meaning to get such the first table, I have written some other queries look like the following
with mytab_cte(user, editionID, size, CompanyName, cvr, postcode, agreement, ActivationT, DeactivationT)
as
(
select ops.Opsaetning as opsaetning, A.EditionID as editionID, b.FilStr as size, v.Navn as CompanyName, v.CVR as cvr, v.Postnr as postcode, A.AftaleNr, BMA.Tilmeldt as ActivationT, BMA.Frameldes as DeactivationT
from BilagsAttachment as b
inner join Opsaetning as ops on b.Opsaetning = ops.Opsaetning
inner join Virksomhed as v on v.Aftalenr = ops.Aftalenr
inner join Aftalenr as A on A.AftaleNr = v.Aftalenr
inner join BenyttetModulAftalenr as BMA on BMA.Aftalenr = ops.Aftalenr
where ISNULL(v.SkalFaktureres, 0) = 1
AND ISNULL(v.ProeveVirksomhed, 0) = 0
AND A.Spaerret = 0
AND (A.FrameldingsDato IS NULL
OR A.FrameldingsDato > GETDATE())
AND v.TilSletning = 0
AND V.Email
)
This looks like SQL server. In which case...
select * from
(
select *,
row_number() over
(
partition by [user]
order by datediff(d,isnull(deactivation, getdate()), activation) desc
) as rn
from yourtable
) v
where rn = 1
Try this:
;WITH DiffCTE
AS
(
SELECT *,
datediff(d,isnull(deactivation, getdate()), activation)
AS Time_Difference
FROM TableName
), MaxDiffs
AS
(
SELECT t1.*
FROM DiffCTE t1
INNER JOIN
(
SELECT user, MAX(Time_Difference) MAXTime_Difference
FROM DiffCTE
GROUP BY user
) t2 ON t2.user = t2.user AND t1. Time_Difference = t2.MAXTime_Difference
)
SELECT user, activation, Deactivation, usage, dmb
FROM MaxDiffs;
Or: using ranking functions you can do that:
;WITH cte
AS
(
SELECT *, ROW_NUMBER() OVER(Partition by [user] ORDER BY datediff(d,isnull(deactivation, getdate()), activation) DESC) row_num
FROM #t
)
SELECT * FROM cte WHERE row_num = 1;
Here is a live demo