Returning a single date with max(date) - sql-server

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 <> ''

Related

snowflake unsupported subquery cannot be evaluated

/Table TEMP has customer hash, effective start date and effective end date. Table CDTLS has customer hash, effective start date.I want to customer hash, effective from, Customer name from TEMP and CDTLS. I am calculating CDTLS end date on the fly and comparing it with TEMP.EFFECTIVE_FROM and TEMP_EFFECTIVE_TO dates. I get an error that unsupported subquery cannot be evaluated./
SELECT
TEMP.CUSTOMER_HASH,
TEMP.EFFECTIVE_FROM,
TEMP.EFFECTIVE_TO,
CDTLS.NAME
FROM TEMP
LEFT CDTLS
ON
TEMP.CUSTOMER_HASH = CDTLS.CUSTOMER_HASH
AND
CDTLS.EFFECTIVE_FROM <= TEMP.EFFECTIVE_FROM
AND
(
SELECT VW.EFFECTIVE_TO FROM
(
SELECT CUSTOMER_HASH, EFFECTIVE_FROM, LEAD(EFFECTIVE_FROM, 1, '9999-12-31') OVER (PARTITION
BY CUSTOMER_HASH ORDER BY EFFECTIVE_FROM ASC) AS EFFECTIVE_TO
FROM CUST_DETAILS
) AS VW
WHERE CDTLS.CUSTOMER_HASH = VW.CUSTOMER_HASH AND CDTLS.EFFECTIVE_FROM = VW.EFFECTIVE_FROM
) >= TEMP.EFFECTIVE_TO
;
I suppose you wanted to run this query:
SELECT
TEMP.CUSTOMER_HASH,
TEMP.EFFECTIVE_FROM,
TEMP.EFFECTIVE_TO,
CDTLS.NAME
FROM TEMP
LEFT join CDTLS
ON
TEMP.CUSTOMER_HASH = CDTLS.CUSTOMER_HASH
AND
CDTLS.EFFECTIVE_FROM <= TEMP.EFFECTIVE_FROM
left join (
SELECT CUSTOMER_HASH, EFFECTIVE_FROM, LEAD(EFFECTIVE_FROM, 1, '9999-12-31') OVER (PARTITION
BY CUSTOMER_HASH ORDER BY EFFECTIVE_FROM ASC) AS EFFECTIVE_TO
FROM CUST_DETAILS
) AS VW on CDTLS.CUSTOMER_HASH = VW.CUSTOMER_HASH AND CDTLS.EFFECTIVE_FROM = VW.EFFECTIVE_FROM
where
VW.EFFECTIVE_TO >= TEMP.EFFECTIVE_TO
You could try using MIN / MAX / LISTAGG etc in the select query to make it deterministically scalar to check if that helps.
https://docs.snowflake.net/manuals/user-guide/querying-subqueries.html#differences-between-correlated-and-non-correlated-subqueries

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

Conversion failed when converting the varchar value 'RN' to data type int

Im getting a conversion error when using the rownumber function on sql-server. I know plenty of similar threads have been posted but ive looked at them and they have different issues.
Conversion failed when converting the varchar value 'RN' to data type int.
It doesnt tell me where the problem is occuring. Does anyone have an idea of where it could be going wrong apart from the rownumber function?
This CTE is getting a previous transaction
, PRETRANSACTIONS AS (
SELECT DISTINCT
CONT.POH_ID AS POH_ID
,CONT.POH_POLICYNUMBER AS Contract_Number
,row_number() over (
partition by CONT.POH_ID
order by TRANS.txh_effectivedate desc
) AS 'RN'
,TXN_STAT.Txs_DESCRIPTION_I AS Transaction_Status_2
,TRANS.txh_effectivedate AS Transaction_Date_2
,TXN_DES.Txt_DESCRIPTION_I AS Transaction_Type_2
,TRX_RES.TRE_AMTPROCESSED AS Transaction_Amount_2
From Se2FAST.dbo.Cm_Opt_Poh_PolicyHdr_S CONT
JOIN BASE BASE
ON BASE.Poh_ID = CONT.POH_ID
INNER JOIN Se2FAST.dbo.Cm_Opt_Pch_PolicyCovHdr_S policyCov
ON CONT.Poh_ID = policyCov.Pch_POLICYHDRID
AND policycov.pch_sequence = 1
INNER JOIN [dbo].[Cm_Sys_Pst_PolicyStatus_I] PST
ON PST.Pst_ID_I = CONT.Poh_Status
LEFT JOIN se2Fast.DBO.CM_OPT_TXH_TRXHDR_S TRANS
ON TRANS.TXH_POLICYHDRID = CONT.POH_ID
AND TRANS.txh_effectivedate < ( SELECT TOP 1 TRAN1.txh_effectivedate
FROM se2Fast.DBO.CM_OPT_TXH_TRXHDR_S TRAN1
WHERE TRAN1.TXH_POLICYHDRID = CONT.POH_ID
ORDER BY txh_effectivedate DESC)
LEFT JOIN se2Fast.dbo.Cm_Opt_Tre_TrxRes_S AS TRX_RES
ON TRANS.Txh_ID = TRX_RES.Tre_TRXHDRID
LEFT JOIN se2Fast.dbo.Cm_Sys_Txt_TrxType_I TXN_DES
ON TRANS.Txh_TRXTYPEID = TXN_DES.Txt_ID_I
--AND TXN_DES.Txt_DESCRIPTION_I NOT IN (
--'Anniversary','Monthiversary','Quarterversary','Calendar Year End')
--AND TXN_DES.Txt_DESCRIPTION_I IS NOT NULL
LEFT JOIN se2Fast.dbo.Cm_Sys_Txs_TrxStatus_I TXN_STAT
ON Trans.Txh_TRXSTATUS = TXN_STAT.Txs_ID_I
AND TXN_STAT.Txs_DESCRIPTION_I = 'Completed'
WHERE 'RN' = 2
)
WHERE 'RN' = 2 is comparing two values, the string 'RN' and the integer 2. When SQL Server parses the expression, it is attempting to convert the literal value 'RN' to a numeric value to perform the integer comparison. Obviously, RN cannot be converted to a numeric value so you are getting an error.
In SQL Server, aliases are materialized after the WHERE/GROUP/HAVING clauses, but before the ORDER BY clause. This means you can't use the alias within the filter of the query it was assigned. You need to generate your column alias in a subquery before you can use it in a comparison.
It looks like you've mixed up your query. You've started as CTE named 'PRETRANSACTIONS', but then you've tried to alias it as a column after the closing parenthesis, AS 'RN'. A CTE must be referenced in the FROM before you can use the columns in the SELECT. If this is just a column expression, get rid of 'PRETRANSACTIONS AS' at the beginning.
WITH PRETRANSACTIONS AS (
SELECT DISTINCT
CONT.POH_ID AS POH_ID
,CONT.POH_POLICYNUMBER AS Contract_Number
,row_number() over (
partition by CONT.POH_ID
order by TRANS.txh_effectivedate desc)
AS RN)
SELECT RN,
TXN_STAT.Txs_DESCRIPTION_I AS Transaction_Status_2
,TRANS.txh_effectivedate AS Transaction_Date_2
,TXN_DES.Txt_DESCRIPTION_I AS Transaction_Type_2
,TRX_RES.TRE_AMTPROCESSED AS Transaction_Amount_2
From Se2FAST.dbo.Cm_Opt_Poh_PolicyHdr_S CONT
JOIN BASE BASE
ON BASE.Poh_ID = CONT.POH_ID
JOIN PRETRANSACTIONS
ON BASE.POH_ID = PRETRANSACTIONS.POH_ID
INNER JOIN Se2FAST.dbo.Cm_Opt_Pch_PolicyCovHdr_S policyCov
ON CONT.Poh_ID = policyCov.Pch_POLICYHDRID
AND policycov.pch_sequence = 1
INNER JOIN [dbo].[Cm_Sys_Pst_PolicyStatus_I] PST
ON PST.Pst_ID_I = CONT.Poh_Status
LEFT JOIN se2Fast.DBO.CM_OPT_TXH_TRXHDR_S TRANS
ON TRANS.TXH_POLICYHDRID = CONT.POH_ID
AND TRANS.txh_effectivedate < ( SELECT TOP 1 TRAN1.txh_effectivedate
FROM se2Fast.DBO.CM_OPT_TXH_TRXHDR_S TRAN1
WHERE TRAN1.TXH_POLICYHDRID = CONT.POH_ID
ORDER BY txh_effectivedate DESC)
LEFT JOIN se2Fast.dbo.Cm_Opt_Tre_TrxRes_S AS TRX_RES
ON TRANS.Txh_ID = TRX_RES.Tre_TRXHDRID
LEFT JOIN se2Fast.dbo.Cm_Sys_Txt_TrxType_I TXN_DES
ON TRANS.Txh_TRXTYPEID = TXN_DES.Txt_ID_I
--AND TXN_DES.Txt_DESCRIPTION_I NOT IN (
--'Anniversary','Monthiversary','Quarterversary','Calendar Year End')
--AND TXN_DES.Txt_DESCRIPTION_I IS NOT NULL
LEFT JOIN se2Fast.dbo.Cm_Sys_Txs_TrxStatus_I TXN_STAT
ON Trans.Txh_TRXSTATUS = TXN_STAT.Txs_ID_I
AND TXN_STAT.Txs_DESCRIPTION_I = 'Completed'
WHERE PRETRANSACTIONS.RN = 2
On a separate note, never use DISTINCT in the same set as ROW_NUMBER. ROW_NUMBER generates a unique number for every row, therefore every row is distinct. All that will do is add sorting overhead without eliminating any rows. You can use RANK or DENSE_RANK instead.
dont use 'RN', just RN without quotes.
You are getting this error because you are comparing the Text value 'RN' with an Integer value 2 in the where clause. But since 'RN' is a column and you want to return all the files that have a value 2 in the field 'RN' instead of
WHERE **'RN'** = 2
just type
WHERE RN = 2
RN without quotes as it is the name of the column.

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

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

SELECT most recent date out of group

I have a T-SQL query that is designed to weed out duplicate entries of a certain product training, grabbing only the one with the most recent DateTaken. For example, if someone has taken a certain training course 3 times, we only want to display one row, that row being the one that contains the most recent DateTaken. Here is what I have so far, however I am receiving the following error:
An expression of non-boolean type specified in a context where a condition is expected, near 'ORDER'.
The ORDER BY is necessary since we want to group all the results of this query by the expiration date. Below is the full query:
SELECT DISTINCT
p.ProductDescription as ProductDesc,
c.CourseDescription as CourseDesc,
c.Partner, a.DateTaken, a.DateExpired, p.Status
FROM
sNumberToAgentId u, AgentProductTraining a, Course c, Product p
WHERE
#agentId = u.AgentId
and u.sNumber = a.sNumber
and a.CourseCode = c.CourseCode
and (a.DateExpired >= #date or a.DateExpired IS NULL)
and a.ProductCode = p.ProductCode
and (p.status != 'D' or p.status IS NULL)
GROUP BY
(p.ProductDescription)
HAVING
MIN(a.DateTaken)
ORDER BY
DateExpired ASC
EDIT
I've made the following changes to the GROUP BY and HAVING clauses, however I am still receiving errors:
GROUP BY
(p.ProductDescription, c.CourseDescription)
HAVING
MIN(a.DateTaken) > GETUTCDATE()
In SQL Management Studio, and red line error marker appears under the ',' after p.ProductDescription, the ')' after c.CourseDescription, the 'a' in a.DateTaken, and the closing parenthesis ')' of GETUTCDATE(). If I simply leave the GROUP BY statement to include only p.ProductDescription I get this error message:
Column 'Product.ProductDescription' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I'm relatively new to SQL, could someone explain what's going on? Thank you!
My suggestion since you are using sql server is to implement row_number() and partition by the ProductDescription and CourseDescription. This will go in a subquery and then you apply a filter to return only those where the row number is equal to one or the most recent record:
select *
from
(
SELECT p.ProductDescription as ProductDesc,
c.CourseDescription as CourseDesc,
c.Partner, a.DateTaken, a.DateExpired, p.Status
row_number() over(partition by p.ProductDescription, c.CourseDescription order by a.DateTaken desc) rn
FROM sNumberToAgentId u
INNER JOIN AgentProductTraining a
ON u.sNumber = a.sNumber
AND (a.DateExpired >= #date or a.DateExpired IS NULL)
INNER JOIN Course c
ON a.CourseCode = c.CourseCode
INNER JOIN Product p
ON a.ProductCode = p.ProductCode
AND (p.status != 'D' or p.status IS NULL)
WHERE u.AgentId = #agentId
) src
where rn = 1
order by DateExpired
Its this line
HAVING MIN(a.DateTaken)
Should be a boolean type such as
HAVING MIN(a.DateTaken) > GETUTCDATE()
Have to return True or a False (Boolean)
Here is the final query I wound up using. It is similar to the suggestions above:
SELECT ProductDesc, CourseDesc, Partner, DateTaken, DateExpired, Status
FROM(
SELECT
p.ProductDescription as ProductDesc,
c.CourseDescription as CourseDesc,
c.Partner, a.DateTaken, a.DateExpired, p.Status,
row_number() OVER (PARTITION BY p.ProductDescription, c.CourseDescription ORDER BY abs(datediff(dd, DateTaken, GETDATE()))) as Ranking
FROM
sNumberToAgentId u, AgentProductTraining a, Course c, Product p
WHERE
#agentId = u.AgentId
and u.sNumber = a.sNumber
and a.CourseCode = c.CourseCode
and (a.DateExpired >= #date or a.DateExpired IS NULL)
and a.ProductCode = p.ProductCode
and (p.status != 'D' or p.status IS NULL)
) aa
WHERE Ranking = '1'

Resources