lag() and lead() sql server function to select data from diff table - sql-server

I need help to be able to do the following:
MainTable has
ID (Primary Key)
DATE_TIME
STATE
SubTable has
ID (Primary Key)
DATE_TIME
LINE1
When MainTable.STATE value changes from 100 to 200, I want to select SubTable.Line1 value where MainTable.DATE_TIME = SubTable.DATE_TIME
It seems that either LAG() or LEAD() function can detects this transition of data in MainTable.STATE field.
How can do this with SQL statement using LAG() or LEAD() function?
Thanks for all the quick responses. Since MainTable.STATE value could be anything, I can't include its value in the sql stmt.

This should do the trick.
SELECT Line1
FROM SubTable s
INNER JOIN
(
SELECT DATE_TIME, State, LAG(State) OVER(ORDER BY DATE_TIME) as LastState
FROM MainTable
)
m
ON s.DATE_TIME = m.DATE_TIME
WHERE State = 200
AND LastState = 100

Assuming I understand your question, you can do something like this:
select Line1
from (
select T1.[Id] as T1Id,
T1.[Key] as T1Key,
T1.[State] as T1State,
T2.[Id] as T2Id,
T2.[FKey] as T2Key,
T2.[Line] as T2Line1,
lag(T1.[State], 1, 100) OVER (order by T1.[Id]) as PriorState
from T1
left outer join T2 on T1.[Key] = T2.FKey
) as Data
where [T1State] <> PriorState
and [T1State] = 200

Here is a solution:
SELECT *, CASE WHEN State = 200 AND LAG(State) OVER(ORDER BY ID) = 100
THEN (SELECT TOP 1 Line1 FROM SubTable WHERE DATE_TIME = m.DATE_TIME) END
FROM MainTable m

Related

Display of online users on the system

I don't know exactly where I'm wrong, but I need a list of all the workers who are currently at work (for the current day), this is my sql query:
SELECT
zp.ID,
zp.USER_ID,
zp.Arrive,
zp.Deppart,
zp.DATUM
FROM time_recording as zp
INNER JOIN personal AS a on zp.USER_ID, = zp.USER_ID,
WHERE zp.Arrive IS NOT NULL
AND zp.Deppart IS NULL
AND zp.DATUM = convert(date, getdate())
ORDER BY zp.ID DESC
this is what the data looks like with my query:
For me the question is, how can I correct my query so that I only get the last Arrive time for the current day for each user?
In this case to get only these values:
Try this below script using ROW_NUMBER as below-
SELECT * FROM
(
SELECT zp.ID, zp.USER_ID, zp.Arrive, zp.Deppart, zp.DATUM,
ROW_NMBER() OVER(PARTITION BY zp.User_id ORDER BY zp.Arrive DESC) RN
FROM time_recording as zp
INNER JOIN personal AS a
on zp.USER_ID = zp.USER_ID
-- You need to adjust above join relation as both goes to same table
-- In addition, as you are selecting nothing from table personal, you can drop the total JOIN part
WHERE zp.Arrive IS NOT NULL
AND zp.Deppart IS NULL
AND zp.DATUM = convert(date, getdate())
)A
WHERE RN =1
you can try this:
SELECT DISTINCT
USER_ID,
LAR.LastArrive
FROM time_recording as tr
CROSS APPLY (
SELECT
MAX(Arrive) as LastArrive
FROM time_recording as ta
WHERE
tr.USER_ID = ta.USER_ID AND
ta.Arrive IS NOT NULL
) as LAR

VIEWS with SELECT inside conditions delaying the query

In one of my SQL views I am using an inline select statement with a where clause.
The outline of my view is like
ALTER VIEW [dbo].[vw_autumn]
AS
SELECT
BookNumber, Title, shopNo
FROM
(SELECT
BookNumber, Title, shopNO
FROM
(SELECT DISTINCT
(sum_vnr) AS BookNumber,
navn1 AS Title,
tik AS ShopNO,
ROW_NUMBER() OVER (PARTITION BY sum_vnr, tik ORDER BY sum_vnr DESC) AS rownumber
FROM
sum s
INNER JOIN
hod h ON s.tik = h.tik
WHERE
s.aar = (SELECT currentyear
FROM SemesterInfo
WHERE SemName = 'Autumn')
AND CAST(s.sum_vnr AS BIGINT) > 10000
AND (s.id LIKE 'h%' OR s.id LIKE 'H%' OR s.id LIKE 'j%'
OR s.id LIKE 'J%')) a
WHERE rownumber = 1
) b
LEFT JOIN (
------
) p ON b.ShopNO = p.tikk
AND b.ISBN = p.vnr
LEFT JOIN table_k k ON p.aar = k.aar
GO
And if I remove the WHERE clause of
WHERE
s.aar = (SELECT currentyear
FROM SemesterInfo
WHERE SemName = 'Autumn')
and shorten it to
WHERE s.aar =19
I am getting the result of view very quickly. But I am trying to add some dynamic nature to this query and selecting this constant from a settings table
Any thoughts on this? Why is the query taking an indefinite time to load with an inline Where clause?
:try with IN insted of =
WHERE
s.aar in (SELECT currentyear
FROM SemesterInfo
WHERE SemName = 'Autumn')
Rewrite the subquery as a join.
INNER JOIN SemesterInfo si
ON s.aer = si.currentYear
WHERE si.SemName = 'Autumn'
If that doesn't do it, consider keeping this syntax and creating an index on SemName

Returning a single date with max(date)

I have the following query. I want to retrieve a list of unique Object ID's with the value closest to a specified date:
INSERT INTO #temp
(
[Object ID]
,[Waarde]
,[Kenmerk]
)
select DISTINCT PME.OBJECTID,
LEFT(PME.OBJECTSCORINGVALUE,LEN(PME.OBJECTSCORINGVALUE)-2),
'P3'
FROM PMEOBJECTSCORINGPOINTS PME
LEFT JOIN PMEOBJECTSCORINGHISTORY PMEH ON PME.OBJECTSCORINGHISTORYID = PMEH.OBJECTSCORINGHISTORYID
INNER JOIN(SELECT OBJECTSCORINGHISTORYID, MAX(DATE) DATE
FROM PMEOBJECTSCORINGHISTORY
WHERE DATE < DATEFROMPARTS(YEAR(getdate())-1, 12, 31)
GROUP BY OBJECTSCORINGHISTORYID) P3 ON PME.OBJECTSCORINGHISTORYID = P3.OBJECTSCORINGHISTORYID
AND PMEH.DATE = P3.DATE
AND PME.ATTRIBUTEID = 'Energie-idx'
AND PME.OBJECTSCORINGVALUE <> ''
------------------
select * from #temp
order by [Object ID], [Kenmerk] ASC
When a certain Object ID only has one known value before 2019-12-31, I get one record in the result set. However, if an Object ID has two (or more) known values before that date, I still get multiple results instead of the value for the date closest to 2019-12-31.
Any pointers on how to get the desired results? Thanks in advance!
(edit: apologies for the bad readibility on the code, thanks for fixing it)
Use analytical funtion ROW_NUMBER(), if you can sort on a column, Perhaps P3.Date or PME.OBJECTSCORINGVALUE
select Objectid, OBJECTSCORINGVALUE,P3
from(
select PME.OBJECTID,
LEFT(PME.OBJECTSCORINGVALUE,LEN(PME.OBJECTSCORINGVALUE)-2) OBJECTSCORINGVALUE,
'P3' Pcol,P3.Date
row_number() over (partition by PME.OBJECTID, LEFT(PME.OBJECTSCORINGVALUE,LEN(PME.OBJECTSCORINGVALUE)-2) order by P3.Date DESC) rn
FROM PMEOBJECTSCORINGPOINTS PME
LEFT JOIN PMEOBJECTSCORINGHISTORY PMEH ON PME.OBJECTSCORINGHISTORYID = PMEH.OBJECTSCORINGHISTORYID
INNER JOIN(SELECT OBJECTSCORINGHISTORYID, MAX(DATE) DATE
FROM PMEOBJECTSCORINGHISTORY
WHERE DATE < DATEFROMPARTS(YEAR(getdate())-1, 12, 31)
GROUP BY OBJECTSCORINGHISTORYID) P3 ON PME.OBJECTSCORINGHISTORYID = P3.OBJECTSCORINGHISTORYID
AND PMEH.DATE = P3.DATE
AND PME.ATTRIBUTEID = 'Energie-idx'
AND PME.OBJECTSCORINGVALUE <> ''
) where rn=1
Here I order the replies with P3.Date, and return only the one with the highest value.
It is guaranteed to only return one row, however you have to be sure about your data to be sure that it is deterministic
I fixed the problem. Turned out I did an incorrect join (wrong level of granularity). I should have done it on OBJECTID instead of OBJECTSCORINGHISTORYID. The result was that the max(date) was returned for OBJECTSCORINGHISTORYID instead of on the level of OBJECTID.
This is the correct query:
INSERT INTO #temp
(
[Object ID]
,[Waarde]
,[Kenmerk]
)
select PME.OBJECTID,
LEFT(PME.OBJECTSCORINGVALUE,LEN(PME.OBJECTSCORINGVALUE)-2),
'P3'
FROM PMEOBJECTSCORINGPOINTS PME
LEFT JOIN PMEOBJECTSCORINGHISTORY PMEH ON PME.OBJECTSCORINGHISTORYID = PMEH.OBJECTSCORINGHISTORYID
INNER JOIN(SELECT OBJECTID, MAX(DATE) DATE
FROM PMEOBJECTSCORINGHISTORY
WHERE DATE < DATEFROMPARTS(YEAR(getdate())-1, 12, 31)
GROUP BY OBJECTID) P3 ON PME.OBJECTID = P3.OBJECTID
AND PMEH.DATE = P3.DATE
AND PME.ATTRIBUTEID = 'Energie-idx'
AND PME.OBJECTSCORINGVALUE <> ''

Clean up SQL Server script

SELECT pp.pat_key, MAX(pp.PROV_NPI) [Provider_ID], CONCAT(pp.LAST_NM,' ',pp.FIRST_NM) [Provider_Name]
INTO pat_primary_provider
FROM TRDW.dbo.PATIENT_PROVIDER pp
WHERE IS_PCP=1
AND pat_key IN (SELECT Consumer_ID FROM CareWire0521)
GROUP BY pp.PAT_KEY, pp.last_nm, pp.FIRST_NM;
SELECT ppp.*
INTO ppp1
FROM (SELECT PAT_KEY, MAX(provider_ID) AS maxprov FROM pat_primary_provider GROUP BY PAT_KEY) AS x
INNER JOIN pat_primary_provider AS ppp ON ppp.PAT_KEY = x.PAT_KEY AND ppp.Provider_ID = x.maxprov;
I need to get the results of ppp1 only using one query (no INTO statements) in SQL Server. Please help.
Simply put the first query into a CTE (without the INTO clause). Then select from that.
;WITH pat_primary_provider AS
(
-- The first query goes here
)
-- The second query goes here
But something like below might also return the PAT_KEY's with the maximum PROV_NPI:
SELECT TOP 1 WITH TIES
PAT_KEY,
MAX(PROV_NPI) AS [Max_Provider_ID],
CONCAT(LAST_NM,' ',FIRST_NM) AS [Patient_Provider_Full_Name]
FROM TRDW.dbo.PATIENT_PROVIDER pp
WHERE IS_PCP = 1
AND PAT_KEY IN (SELECT Consumer_ID FROM CareWire0521)
GROUP BY PAT_KEY, LAST_NM, FIRST_NM
ORDER BY row_number() over (order by MAX(PROV_NPI) desc);
Whats wrong with just inserting the first query as subqueries into the second?
SELECT ppp.*
FROM (SELECT PAT_KEY, MAX(provider_ID) AS maxprov FROM (SELECT pp.pat_key, MAX(pp.PROV_NPI) [Provider_ID], CONCAT(pp.LAST_NM,' ',pp.FIRST_NM) [Provider_Name]
FROM TRDW.dbo.PATIENT_PROVIDER pp
WHERE IS_PCP=1
AND pat_key IN (SELECT Consumer_ID FROM CareWire0521)
GROUP BY pp.PAT_KEY, pp.last_nm, pp.FIRST_NM) GROUP BY PAT_KEY) AS x
INNER JOIN (SELECT pp.pat_key, MAX(pp.PROV_NPI) [Provider_ID], CONCAT(pp.LAST_NM,' ',pp.FIRST_NM) [Provider_Name]
FROM TRDW.dbo.PATIENT_PROVIDER pp
WHERE IS_PCP=1
AND pat_key IN (SELECT Consumer_ID FROM CareWire0521)
GROUP BY pp.PAT_KEY, pp.last_nm, pp.FIRST_NM) AS ppp ON ppp.PAT_KEY = x.PAT_KEY AND ppp.Provider_ID = x.maxprov;

Update records SQL?

First when I started this project seemed very simple. Two tables, field tbl1_USERMASTERID in Table 1 should be update from field tbl2_USERMASTERID Table 2. After I looked deeply in Table 2, there is no unique ID that I can use as a key to join these two tables. Only way to match the records from Table 1 and Table 2 is based on FIRST_NAME, LAST_NAME AND DOB. So I have to find records in Table 1 where:
tbl1_FIRST_NAME equals tbl2_FIRST_NAME
AND
tbl1_LAST_NAME equals tbl2_LAST_NAME
AND
tbl1_DOB equals tbl2_DOB
and then update USERMASTERID field. I was afraid that this can cause some duplicates and some users will end up with USERMASTERID that does not belong to them. So if I find more than one record based on first,last name and dob those records would not be updated. I would like just to skip and leave them blank. That way I wouldn't populate invalid USERMASTERID. I'm not sure what is the best way to approach this problem, should I use SQL or ColdFusion (my server side language)? Also how to detect more than one matching record?
Here is what I have so far:
UPDATE Table1 AS tbl1
LEFT OUTER JOIN Table2 AS tbl2
ON tbl1.dob = tbl2.dob
AND tbl1.fname = tbl2.fname
AND tbl1.lname = tbl2.lname
SET tbl1.usermasterid = tbl2.usermasterid
WHERE LTRIM(RTRIM(tbl1.usermasterid)) = ''
Here is query where I tried to detect duplicates:
SELECT DISTINCT
tbl1.FName,
tbl1.LName,
tbl1.dob,
COUNT(*) AS count
FROM Table1 AS tbl1
LEFT OUTER JOIN Table2 AS tbl2
ON tbl1.dob = tbl2.dob
AND tbl1.FName = tbl2.first
AND tbl1.LName = tbl2.last
WHERE LTRIM(RTRIM(tbl1.usermasterid)) = ''
AND LTRIM(RTRIM(tbl1.first)) <> ''
AND LTRIM(RTRIM(tbl1.last)) <> ''
AND LTRIM(RTRIM(tbl1.dob)) <> ''
GROUP BY tbl1.FName,tbl1.LName,tbl1.dob
Some data after I tested query above:
First Last DOB Count
John Cook 2008-07-11 2
Kate Witt 2013-06-05 1
Deb Ruis 2016-01-22 1
Mike Bennet 2007-01-15 1
Kristy Cruz 1997-10-20 1
Colin Jones 2011-10-13 1
Kevin Smith 2010-02-24 1
Corey Bruce 2008-04-11 1
Shawn Maiers 2016-08-28 1
Alenn Fitchner 1998-05-17 1
If anyone have idea how I can prevent/skip updating duplicate records or how to improve this query please let me know. Thank you.
You could check for and avoid duplicate matches using with common_table_expression (Transact-SQL)
along with row_number()., like so:
with cte as (
select
t.fname
, t.lname
, t.dob
, t.usermasterid
, NewUserMasterId = t2.usermasterid
, rn = row_number() over (partition by t.fname, t.lname, t.dob order by t2.usermasterid)
from table1 as t
inner join table2 as t2 on t.dob = t2.dob
and t.fname = t2.fname
and t.lname = t2.lname
and ltrim(rtrim(t.usermasterid)) = ''
)
--/* confirm these are the rows you want updated
select *
from cte as t
where t.NewUserMasterId != ''
and not exists (
select 1
from cte as i
where t.dob = i.dob
and t.fname = i.fname
and t.lname = i.lname
and i.rn>1
);
--*/
/* update those where only 1 usermasterid matches this record
update t
set t.usermasterid = t.NewUserMasterId
from cte as t
where t.NewUserMasterId != ''
and not exists (
select 1
from cte as i
where t.dob = i.dob
and t.fname = i.fname
and t.lname = i.lname
and i.rn>1
);
--*/
I use the cte to extract out the sub query for readability. Per the documentation, a common table expression (cte):
Specifies a temporary named result set, known as a common table expression (CTE). This is derived from a simple query and defined within the execution scope of a single SELECT, INSERT, UPDATE, or DELETE statement.
Using row_number() to assign a number for each row, starting at 1 for each partition of t.fname, t.lname, t.dob. Having those numbered allows us to check for the existence of duplicates with the not exists() clause with ... and i.rn>1
You could use a CTE to filter out the duplicates from Table1 before joining:
; with CTE as (select *
, count(ID) over (partition by LastName, FirstName, DoB) as IDs
from Table1)
update a
set a.ID = b.ID
from Table2 a
left join CTE b
on a.FirstName = b.FirstName
and a.LastName = b.LastName
and a.Dob = b.Dob
and b.IDs = 1
This will work provided there are no exact duplicates (same demographics and same ID) in table 1. If there are exact duplicates, they will also be excluded from the join, but you can filter them out before the CTE to avoid this.
Please try below SQL:
UPDATE Table1 AS tbl1
INNER JOIN Table2 AS tbl2
ON tbl1.dob = tbl2.dob
AND tbl1.fname = tbl2.fname
AND tbl1.lname = tbl2.lname
LEFT JOIN Table2 AS tbl3
ON tbl3.dob = tbl2.dob
AND tbl3.fname = tbl2.fname
AND tbl3.lname = tbl2.lname
AND tbl3.usermasterid <> tbl2.usermasterid
SET tbl1.usermasterid = tbl2.usermasterid
WHERE LTRIM(RTRIM(tbl1.usermasterid)) = ''
AND tbl3.usermasterid is null

Resources