Bad performance with sql query - sql

I have a snippet of SQL that compared the last two records and gives the datediff in seconds, however the way I have it is quite slow taking up to 20 seconds to execute depending how many controllerID's I needs to check.
What would be a more efficient way of do doing this?
select
T.controllerID,
datediff(ss, T.Max_dtReading, T1.Max_dtReading) As ElaspedTime
from
(select
controllerID,
max(dtReading) as Max_dtReading
from
ReaderData
where
CardID = 'FFFFFFF0' AND (controllerID in(2,13,28,30,37,40))
group by
controllerID) as T
outer apply
(select
max(T1.dtReading) as Max_dtReading
from
ReaderData as T1
where
T1.CardID = 'FFFFFFF0' AND (controllerID in(2,13,28,30,37,40))
and T1.controllerID = T.controllerID
and T1.dtReading < T.Max_dtReading) as T1

I might suggest conditional aggregation for this:
select controllerID,
datediff(second, max(dtReading), min(dtReading)
) As ElaspedTime
from (select controllerID, dtReading,
row_number() over (partition by controllerID order by dtReading desc) as seqnum
from ReaderData
where CardID = 'FFFFFFF0' AND
controllerID in (2, 13, 28, 30, 37, 40)
) r
where seqnum <= 2
group by controllerID

You can use ROW_NUMBER() in order to locate the records with the 2 highest dtReading values, then join these together to calculate the difference:
;WITH CTE AS (
SLEECT controllerID, dtReading,
ROW_NUMBER() OVER (PARTITION BY controllerID
ORDER BY dtReading DESC) AS rn
FROM ReaderData
WHERE CardID = 'FFFFFFF0' AND (controllerID IN (2,13,28,30,37,40))
)
SELECT c1.controllerID,
DATEDIFF(ss, c1.dtReading, c2.dtReading) AS ElaspedTime
FROM CTE c1
INNER JOIN CTE c2 ON (c1.controllerID = c2.controllerID)
AND c1.rn = 1 AND c2.rn = 2

;WITH CTE AS
(select controllerID
,dtReading
,ROW_NUMBER() OVER (PARTITION BY controllerID ORDER BY dtReading DESC) rn
from ReaderData
where CardID = 'FFFFFFF0'
AND controllerID IN (2,13,28,30,37,40)
)
select C1.controllerID
,datediff(ss, C1.dtReading, C2.dtReading) As ElaspedTime
from CTE C1
LEFT JOIN CTE C2 ON C1.controllerID = C2.controllerID
AND C1.rn = 1
AND C1.rn < C2.rn

Related

Bring next value after condition

I am trying to fetch the next value after the condition is found. In this case, it is a row from 13/05/2021 the result I want to see is the row from 19/05/2021 Cte and CTE1 bring correct results.
I can't figure out what is wrong with my query.
<with cte as
(
select
customerid
,max(timestamp) as [Case Submitted]
,row_number() over (partition by [CustomerId] order by [CustomerId] ,max([timestamp]) desc) as rownum
from Table1
where substatus = 'Case Submitted'
and timestamp > '2021-01-01'
Group by
customerid
,timestamp
)
,CTE2 as
(
Select *
from cte
Where rownum = 1
),
CTE3 as
(
select
PS.customerid
,(PS.timestamp) as [Customer Support]
,row_number() over (partition by PS.customerid order by PS.customerid ) as rownum
from Table1 PS
left join CTE2 C2 on C2.customerid = PS.customerid and C2.[Case Submitted] > PS.timestamp and C2.rownum =1
where status = 'Customer Support'
and timestamp > '2021-01-01'
Group by
PS.customerid
,ps.timestamp
)
Select*
from CTE3>
untested notepad scribble
with CTE1 as
(
select
customerid
, [timestamp] as [Case Submitted]
, rownum = row_number() over (partition by CustomerId order by [timestamp] desc)
from Table1
where substatus = 'Case Submitted'
and [timestamp] > cast('2021-01-01' as date)
),
CTE2 AS
(
select
PS.customerid
, PS.timestamp as [Customer Support]
, rownum = row_number() over (partition by PS.customerid order by PS.timestamp)
from Table1 as PS
join CTE1 as C1
on C1.customerid = PS.customerid
and C1.[Case Submitted] > PS.[timestamp]
and C1.rownum = 1
where PS.status = 'Customer Support'
and [timestamp] > cast('2021-01-01' as date)
)
select *
from CTE2
where rownum = 1

PostgreSQL Percent Change using Row Number

I'm trying to find the percent change using row number with PostgreSQL but I'm running into an error where my "percent_change" column shows 0.
Here is what I have as my code.
WITH CTE AS (
SELECT date, sales, ROW_NUMBER() OVER (ORDER by date) AS rn
FROM sales_2019)
SELECT c1.date, c1.sales,
CAST(COALESCE (((c1.sales - c2.sales) * 1.0 / c2.sales) * 100, 0) AS INT) AS percent_change
FROM CTE AS c1
LEFT JOIN CTE AS c2
ON c1.date = c2.date AND c1.rn = c2.rn + 1
Here is my SQL table in case it's needed. Thank you in advance, I greatly appreciate it.
You can use LAG() for your requirement:
select
date,
sales,
round(coalesce((((sales-(lag(sales) over (order by date)))*1.0)/(lag(sales) over (order by date)))*100,0),2)
from sales_2019
or you can try with WITH clause
with cte as ( select
date,
sales,
coalesce(lag(sales) over (order by date),0) as previous_month
from sales_2019
)
select
date,
sales,
round( coalesce( (sales-previous_month)*1.0/nullif(previous_month,0),0 )*100,2)
from cte
DEMO
EDIT as per requirement in comment
with cte as ( select
date_,
sales,
ROW_NUMBER() OVER (ORDER by date_) AS rn1,
ROW_NUMBER() OVER (ORDER by date_)-1 AS rn2
from sales_2019
)
select t1.date_,
t1.sales,
round( coalesce( (t1.sales-t2.sales)*1.0/nullif(t2.sales,0),0 )*100,2)
from cte t1 left join cte t2 on t1.rn2=t2.rn1
DEMO

"First order by" in Teradata

I have a problem converting SQL statement from Oracle to Teradata. In Oracle statement is that:
SELECT ar.account_no,
MAX (ah.bal_acct) KEEP (DENSE_RANK FIRST ORDER BY ah.created_t desc)
FROM ar
JOIN ah ON ah.obj_id0 = ar.poid_Id0
JOIN acc ON a.poid_id0 = ar.account_obj_Id0
WHERE acc.account_no = '1234'
AND ah.created_t <= 1434753495
GROUP BY ar.account_no
I need to do similar statement in Teradata. I tried something with
QUALIFY ROW_NUMBER() OVER( PARTITION BY max(ah.bal_acct) ORDER BY ah.created_t desc) = 1
But all the time I have error: Selected non-aggregate values must be part of the associated group.
This is what I got:
Select ar.account_no, ah.created_t, ah.bal_acct
FROM VD_REPLICA_BRM.pi_tp_acct_ar_t ar
JOIN VD_REPLICA_BRM.pi_tp_acct_ar_hist_T ah ON ah.obj_id0 = ar.poid_Id0
JOIN VD_REPLICA_BRM.pi_account_t acc ON acc.poid_id0 = ar.account_obj_Id0
WHERE acc.account_no = '00003095660515'
AND ah.created_t <= CAST('2016-10-31' AS DATE FORMAT 'YYYY-MM-DD')
QUALIFY ROW_NUMBER() OVER( PARTITION BY max(ah.bal_acct) ORDER BY ah.created_t desc) = 1
GROUP BY ar.account_no
Where do I do mistake?
I'm not sure if you can do this with qualify. An equivalent statement is:
SELECT ar.account_no, ah.created_t, ah.bal_acct
FROM (SELECT ar.account_no, ah.created_t, ah.bal_acct,
ROW_NUMBER() OVER (PARTITION BY ar.account_no ORDER BY ah.created_t DESC) as seqnum
FROM ar JOIN
ah
ON ah.obj_id0 = ar.poid_Id0 JOIN
acc
ON a.poid_id0 = ar.account_obj_Id0
WHERE acc.account_no = '1234' AND ah.created_t <= 1434753495
) t
WHERE seqnum = 1;
Duh. You can do this with QUALIFY. The issue is the GROUP BY:
SELECT ar.account_no, ah.created_t, ah.bal_acct
FROM VD_REPLICA_BRM.pi_tp_acct_ar_t ar JOIN
VD_REPLICA_BRM.pi_tp_acct_ar_hist_T ah
ON ah.obj_id0 = ar.poid_Id0 JOIN
VD_REPLICA_BRM.pi_account_t acc
ON acc.poid_id0 = ar.account_obj_Id0
WHERE acc.account_no = '00003095660515' AND
ah.created_t <= CAST('2016-10-31' AS DATE FORMAT 'YYYY-MM-DD')
QUALIFY ROW_NUMBER() OVER( PARTITION BY ar_account_no ORDER BY ah.created_t desc) = 1

SQL Server 2008 - Finding duplicates using ROW_NUMBER

i have the following SQL which works to find duplicates
SELECT *
FROM (SELECT
id,
ShipAddress,
ShipZIPPostal,
ROW_NUMBER() OVER (PARTITION BY shipaddress, shipzippostal ORDER BY shipaddress) ROWNUM
FROM orders
WHERE CONVERT(date, orderdate) = CONVERT(date, GETDATE())) x
WHERE rownum > 1
I would like to only see rows where, if the value of Rownum > 1 then i would like to see its corresponding row where rownum =1.
So basically, if a row has duplicates, i want to see the original row and all its duplicates.
If a row does not have duplicates, then i don't want to see it (it will have rownum = 1 )
How would i do this please?
cheers
Use count(*) rather than row_number():
SELECT *
FROM (SELECT id, ShipAddress, ShipZIPPostal,
COUNT(*) OVER (PARTITION BY shipaddress, shipzippostal) as cnt
FROM orders
WHERE CONVERT(date, orderdate) = CONVERT(date, GETDATE())
) x
WHERE cnt > 1;
In addition to Gordon's answer, if you want to keep the row_number() approach for some academic reason, you can do this:
SELECT *
FROM (SELECT
id,
ShipAddress,
ShipZIPPostal,
ROW_NUMBER() OVER (PARTITION BY shipaddress, shipzippostal ORDER BY shipaddress) ROWNUM
FROM orders
WHERE CONVERT(date, orderdate) = CONVERT(date, GETDATE())) x
WHERE EXISTS(
SELECT * FROM x x2
WHERE x.shipaddress=x2.shipaddress
AND x.shipzippostal=x2.shipzippostal
AND x2.ROWNUM>1
)
I'd actually prefer a cte structure like this personally:
WITH cte AS (
SELECT
id,
ShipAddress,
ShipZIPPostal,
ROW_NUMBER() OVER (PARTITION BY shipaddress, shipzippostal ORDER BY shipaddress) ROWNUM
FROM orders
WHERE CONVERT(date, orderdate) = CONVERT(date, GETDATE())
)
SELECT *
FROM cte
WHERE EXISTS(
SELECT * FROM cte x2
WHERE cte.shipaddress=x2.shipaddress
AND cte.shipzippostal=x2.shipzippostal
AND x2.ROWNUM>1
)
You could add a second row_number, but change the order by to ID so it will be different, and compare the 2 row_numbers
SELECT
*
FROM
(SELECT
id,
ShipAddress,
ShipZIPPostal,
ROW_NUMBER() OVER (PARTITION BY shipaddress,shipzippostal ORDER BY id) ROWNUM1,
ROW_NUMBER() OVER (PARTITION BY shipaddress,shipzippostal ORDER BY id DESC) ROWNUM2
FROM
orders
WHERE
CONVERT(DATE,orderdate) = CONVERT(DATE,GETDATE())
) x
WHERE
ROWNUM1 <> ROWNUM2

How to select specific rows in PostgreSQL query?

I have written a query to get the 'reason_code' and 'app_count' for top five rows from result set and sum of remaining app_count under name 'others'.
Here what I have tried:
(SELECT a.app_pgm_rsnd_rsn_cd, a.denied_app_count
FROM (SELECT app_pgm_rsnd_rsn_cd ,COUNT(1) as denied_app_count
FROM app_pgm_choice, ead_case
where app_pgm_sts= 'DN'
AND app_pgm_req_dt >= '20150101'
AND app_pgm_req_dt <= '20150130'
AND EAD_CS_APP_NUM = APP_PGM_NUM
AND EAD_CS_SEND_CNTY_ID = '19'
AND EAD_CS_TRAN_STS = 'PE'
GROUP BY app_pgm_rsnd_rsn_cd
ORDER BY denied_app_count desc) a
WHERE ROWNUM <=5 )
UNION ALL
(SELECT 'OTHERS' as app_pgm_rsnd_rsn_cd, SUM(b.denied_app_count) as denied_app_count
FROM (SELECT app_pgm_rsnd_rsn_cd ,COUNT(1) as denied_app_count
FROM app_pgm_choice, ead_case
where app_pgm_sts= 'DN'
AND app_pgm_req_dt >= '20150101'
AND app_pgm_req_dt <= '20150130'
AND EAD_CS_APP_NUM = APP_PGM_NUM
AND EAD_CS_SEND_CNTY_ID = '19'
AND EAD_CS_TRAN_STS = 'PE'
GROUP BY app_pgm_rsnd_rsn_cd
ORDER BY denied_app_count desc) b
WHERE ROWNUM >=5 )
But when I run this query it shows following error message:
ERROR: column "rownum" does not exist
LINE 13: WHERE ROWNUM <=5 )
^
********** Error **********
ERROR: column "rownum" does not exist
SQL state: 42703
Character: 397
What is option for ROWNUM variable?
I think you are looking for the LIMIT clause.
SELECT *
FROM sometable
ORDER BY denied_app_count DESC
LIMIT 5
ROWNUM is an Oracle-ism.
For greater-than, you can use OFFSET:
SELECT *
FROM sometable
ORDER BY denied_app_count DESC
OFFSET 5
to skip the first 5 rows and return the rest.
The two can be, and often are, combined when doing things like pagination.
See LIMIT and OFFSET
In PostgreSQL you can use the row_number() window function, but for your purpose here it's unnecessary. On the other hand, it might be faster to do a single query with row_number then query the result table twice to get the two parts. Try that approach and see if it performs better.
Try this query
(SELECT a.app_pgm_rsnd_rsn_cd, a.denied_app_count
FROM (SELECT app_pgm_rsnd_rsn_cd ,COUNT(1) as denied_app_count
,row_number() OVER (ORDER BY app_pgm_rsnd_rsn_cd ) AS ROWNUM
FROM app_pgm_choice, ead_case
where app_pgm_sts= 'DN'
AND app_pgm_req_dt >= '20150101'
AND app_pgm_req_dt <= '20150130'
AND EAD_CS_APP_NUM = APP_PGM_NUM
AND EAD_CS_SEND_CNTY_ID = '19'
AND EAD_CS_TRAN_STS = 'PE'
GROUP BY app_pgm_rsnd_rsn_cd
ORDER BY denied_app_count desc) a
WHERE ROWNUM <=5 )
UNION ALL
(SELECT 'OTHERS' as app_pgm_rsnd_rsn_cd, SUM(b.denied_app_count) as denied_app_count
FROM (SELECT app_pgm_rsnd_rsn_cd ,COUNT(1) as denied_app_count
,row_number() OVER (ORDER BY app_pgm_rsnd_rsn_cd ) AS ROWNUM
FROM app_pgm_choice, ead_case
where app_pgm_sts= 'DN'
AND app_pgm_req_dt >= '20150101'
AND app_pgm_req_dt <= '20150130'
AND EAD_CS_APP_NUM = APP_PGM_NUM
AND EAD_CS_SEND_CNTY_ID = '19'
AND EAD_CS_TRAN_STS = 'PE'
GROUP BY app_pgm_rsnd_rsn_cd
ORDER BY denied_app_count desc) b
WHERE ROWNUM >=5 )