How to escape several with in the query? - sql

I need to refactor my query, but I have no idea, how to do this.
I can see several duplicates of using the same logic, but I continue to repeat this manner of querying from query to query, and I feel, that this kind of querying becomes my main frame of thinking of SQL and I don't want this.
Could you show me more acceptable variant of this query, so I won't repeat my way of thinking again?
Here it is
WITH fourth_table AS
(
WITH third_table AS
(
WITH second_table AS
(
WITH initial_table AS
(
SELECT
DISTINCT ctr.country_region, EXTRACT (YEAR FROM s.time_id)::int AS calendar_year, chn.channel_desc,
SUM(s.amount_sold) OVER (PARTITION BY ctr.country_region||chn.channel_desc||EXTRACT (YEAR FROM s.time_id)) AS amount_sold
FROM sales s
JOIN channels chn ON s.channel_id = chn.channel_id
JOIN customers c ON s.cust_id = c.cust_id
JOIN countries ctr ON c.country_id = ctr.country_id
WHERE ctr.country_region IN ('Americas','Asia', 'Europe')
AND
EXTRACT (YEAR FROM s.time_id)::int IN (1998, 1999, 2000, 2001)
ORDER BY ctr.country_region, calendar_year, chn.channel_desc
)
SELECT country_region, calendar_year, channel_desc, amount_sold,
(amount_sold/SUM(amount_sold) OVER (PARTITION BY country_region||calendar_year)*100)::decimal(10,2) AS bychannels
FROM initial_table
)
SELECT *,
LAG (bychannels, 4) OVER (ORDER BY 6) AS lower_salary
FROM second_table--correct here smth wrong
)
SELECT *, bychannels - lower_salary AS diff FROM third_table
)
SELECT country_region, calendar_year, channel_desc,
--'FM 999,999,999,990D'
LPAD(to_char(amount_sold, 'FM999,999,999,990 $'),20, ' ') AS amount_sold,
LPAD(bychannels || ' %' ,20, ' ') AS "% BY CHANNELS",
LPAD(lower_salary || ' % ' ,20, ' ') AS "% PREVIOUS PERIOD",
diff AS "% DIFF"
FROM fourth_table WHERE calendar_year NOT IN (1998);

You are mixing CTE (Common Table Queries) with Subqueries, the beauty of the with clause is normally the readability:
with initial_table as
(
SELECT
DISTINCT ctr.country_region, EXTRACT (YEAR FROM s.time_id)::int AS calendar_year, chn.channel_desc,
SUM(s.amount_sold) OVER (PARTITION BY ctr.country_region||chn.channel_desc||EXTRACT (YEAR FROM s.time_id)) AS amount_sold
FROM sales s
JOIN channels chn ON s.channel_id = chn.channel_id
JOIN customers c ON s.cust_id = c.cust_id
JOIN countries ctr ON c.country_id = ctr.country_id
WHERE ctr.country_region IN ('Americas','Asia', 'Europe')
AND
EXTRACT (YEAR FROM s.time_id)::int IN (1998, 1999, 2000, 2001)
ORDER BY ctr.country_region, calendar_year, chn.channel_desc
)
,second_table as
(
SELECT country_region, calendar_year, channel_desc, amount_sold,
(amount_sold/SUM(amount_sold) OVER (PARTITION BY country_region||calendar_year)*100)::decimal(10,2) AS bychannels
FROM initial_table
)
,third_table as
(
SELECT *,
LAG (bychannels, 4) OVER (ORDER BY 6) AS lower_salary
FROM second_table--correct here smth wrong
)
,fourth_table as
(
SELECT *, bychannels - lower_salary AS diff FROM third_table
)
SELECT country_region, calendar_year, channel_desc,
--'FM 999,999,999,990D'
LPAD(to_char(amount_sold, 'FM999,999,999,990 $'),20, ' ') AS amount_sold,
LPAD(bychannels || ' %' ,20, ' ') AS "% BY CHANNELS",
LPAD(lower_salary || ' % ' ,20, ' ') AS "% PREVIOUS PERIOD",
diff AS "% DIFF"
FROM fourth_table WHERE calendar_year NOT IN (1998);

Related

Converting Subquery into Single Query

I've a query that has multiple subqueries with parameters as follows:
SELECT
V.EMPNO,
V.FIRST_NAME || ' ' || V.MIDDLE_NAME
|| ' '
|| V.LAST_NAME FULLNAME,
M.APP_NO,
K.TOTAL_AMT,
K.TOTAL_AMT - (SELECT
SUM(Q.RECAMOUNT)
FROM
LOAN_ADJ_DETAILS_NEW q
WHERE
Q.APP_NO = M.APP_NO
AND Q.RECDATE <= '01-AUG-2022') REMAINING,
(SELECT
SUM(P.RECAMOUNT)
FROM
LOAN_ADJ_DETAILS_NEW p
WHERE
P.APP_NO = M.APP_NO
AND P.RECDATE <= '01-AUG-2022') TOTALADJ,
(SELECT
COUNT(*)
FROM
LOAN_ADJ_DETAILS_NEW p
WHERE
P.APP_NO = M.APP_NO
AND P.RECDATE IS NOT NULL
AND P.RECDATE <= '01-AUG-2022') INSTALLMENT,
(SELECT
MAX(Q.RECAMOUNT)
FROM
LOAN_ADJ_DETAILS_NEW q
WHERE
Q.APP_NO = M.APP_NO
AND Q.RECDATE = '01-AUG-2022') LASTINSTALL,
(SELECT
MAX(S.RECDATE)
FROM
LOAN_ADJ_DETAILS_NEW s
WHERE
S.APP_NO = M.APP_NO
AND S.RECDATE <= '01-AUG-2022') LASTDATE,
(SELECT
SUM(P.RECAMOUNT)
FROM
LOAN_ADJ_DETAILS_NEW p
WHERE
P.APP_NO = M.APP_NO
AND P.RECDATE <= '01-AUG-2022') PAID
FROM
LOAN_ADJ_DETAILS_NEW m,
TBL_LOAN_MASTER k,
EMP_PERSONAL v
WHERE
V.EMPNO = M.EMPNO
AND K.LOAN_ID = M.APP_NO
AND M.RECAMOUNT > 0
AND M.RECDATE IS NOT NULL
AND M.RECDATE = '01-AUG-2022'
AND M.RECDATE >= '01-AUG-2022';
The query is simple, just to get user wise loan information. Now the thing is, I require to make it into one query something as follows:
SELECT * FROM TABLE WHERE COLUMN <= PARAMTER;
The above query will be used as a dynamic query from database to front-end. I was hoping if this could be converted to a single query or view anyway.
You can use analytic functions and combine the sub-queries into a single one in the FROM clause:
SELECT V.EMPNO,
V.FIRST_NAME || ' ' || V.MIDDLE_NAME || ' ' || V.LAST_NAME
AS FULLNAME,
M.APP_NO,
K.TOTAL_AMT,
K.TOTAL_AMT - m.paid AS REMAINING,
m.totaladj,
m.paid,
m.installment,
m.lastinstall,
m.lastdate
FROM ( SELECT app_no,
empno,
recamount,
recdate,
SUM(RECAMOUNT) OVER (PARTITION BY app_no) AS totaladj,
SUM(RECAMOUNT) OVER (PARTITION BY app_no) AS paid,
COUNT(*) OVER (PARTITION BY app_no) AS installment,
MAX(RECAMOUNT) OVER (PARTITION BY app_no) AS lastinstall,
MAX(RECDATE) OVER (PARTITION BY app_no) AS lastdate
FROM LOAN_ADJ_DETAILS_NEW
WHERE RECDATE <= DATE '2022-08-01'
) m
INNER JOIN TBL_LOAN_MASTER k
ON (K.LOAN_ID = M.APP_NO)
INNER JOIN EMP_PERSONAL v
ON (V.EMPNO = M.EMPNO)
WHERE M.RECAMOUNT > 0
AND M.RECDATE = DATE '2022-08-01';

How To select one data from many data (Oracle SQL)

I Have Query
SELECT * FROM PPI_CD WHERE PATIENT_NO = 14683
Results from this query display some of the same data based on PATIENT_NO
How to order only the latest data out? only One
Full Query
SELECT
PPI_ID,
co.order_no,
p.RM_NO,
co.patient_name,
EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM p.DOB) as Age,
p.sex,
co.order_date as TGL_MASUK,
NVL(il.NAME, '0') as ICD,
NVL(ppi.HPA_UC, '0') AS UC,
NVL(ppi.HPA_IVL, '0') AS IVL,
NVL(ppi.HPA_CVL, '0') AS CVL,
NVL(ppi.HPA_ETT, '0') AS ETT,
NVL(ppi.IRS_VAP, '0') AS VAP,
NVL(ppi.IRS_PLEB, '0') AS PLEB,
NVL(ppi.IRS_ISK, '0') AS ISK,
NVL(ppi.IRS_IAD, '0') AS IAD,
NVL(ppi.TB, '0') AS TB,
NVL(ppi.HK, '0') AS HK,
NVL(ppi.AB, '0') AS AB,
NVL(ppi.ANTIBIOTIK , '0') AS ANTIBIOTIK,
NVL(ppi.DEKU, '0') AS DEKU,
NVL(ppi.JK_DARAH , '0') AS DARAH,
NVL(ppi.JK_SWAB , '0') AS SWAB,
NVL(ppi.JK_SPUTUM , '0') AS SPUTUM,
NVL(ppi.JK_URINE , '0') AS URINE,
NVL(ppi.TEMP, 'N/A') AS TEMP
FROM case_orders co
LEFT JOIN PPI_CD cd
ON cd.patient_no = co.patient_no
LEFT JOIN illness_lists il
ON il.illness_no = cd.illness_no
LEFT JOIN patients p
ON p.contact_no = co.patient_no
LEFT JOIN PPI ppi
ON ppi.RM_NO = p.RM_NO
WHERE CO.status_no=5
ORDER BY CO.ORDER_DATE ASC;
the problem is with this
out 2 data which should be only one data
For just one patient, you can order by and a row limiting clause:
select *
from ppi_cd
where patient_no = 14683
order by posted_date desc
fetch first row only
If you want the latest record per patient_no, you can do:
select c.*
from ppi_cd c
where c.posted_date = (
select max(c1.posted_date) from ppi_cd c1 where c1.patient_no = c.patient_no
)
Use row_number() :
select pc.*
from (select pc.*, row_number() over (partition by PATIENT_NO order by pc.post_date DESC) as seq
from PPI_CD pc
) pc
where pc.seq = 1;

Getting latest date in SQL query

I am running this query right now:
WITH Result AS (
select distinct
--pump.Product_PN,
--pump.product_name,
mot.Motor_pn,
Mot.Motor_name,
mas.STLAN AS Usage,
po.DATUV as change_date,
mot.[Procurement type],
mot.Plant as plant,
po.IDNRK AS Component_PN,
mak.MAKTG as component_name
FROM dbo.TBLMAST AS mas
INNER JOIN dbo.TBLSTPO AS po
ON mas.STLNR = po.STLNR
INNER JOIN dbo.TBLMAKT AS mak
ON mak.MATNR = po.IDNRK
--INNER JOIN ['Pumps to motor$'] AS pump
--ON pump.[Motor Product Number] = SUBSTRING(mas.MATNR, PATINDEX('%[^0 ]%', mas.MATNR + ' '), LEN(mas.MATNR))
INNER JOIN Grundfos_motors AS mot
ON mot.Motor_PN = SUBSTRING(mas.MATNR, PATINDEX('%[^0 ]%', mas.MATNR + ' '), LEN(mas.MATNR))
where mas.STLAN = '8' and mot.Motor_PN = '78085617' and mak.MAKTG like '%stator%'
)
select motor_pn, motor_name, plant, change_date, Component_PN, Component_name, cast(getdate() as date) as Date_of_Extraction_date
from result
order by change_date DESC;
The result of this query is this:
From this query result, I want to run another query where the change_date is set to the latest. So I only want to retrieve the data for the latest date and rest is excluded. Something like:
Can anyone help?
You should be able to do:
select motor_pn, motor_name, plant, change_date, Component_PN, Component_name,
cast(getdate() as date) as Date_of_Extraction_date
from (select r.*,
row_number() over (partition by motor_pn order by change_date desc) as seqnum
from result r
) r
where seqnum = 1;

Subtract Max and Min from same column

select
d.FK_SOCRD_ID,
d.SUBJECT_NUMBER,
c.PTLASTNAME || ', ' || c.PTFIRSTNAME as "Patient name",
min(b.created) as Data Entry 1,
max(b.created) as Data Entry 2,
min(b.created) - max(b.created)
|| ' days and '
|| TO_CHAR(to_date('01/01/2000', 'MM-DD-YYYY')
+ (b.created - b.created), 'HH24:MI:SS' ) AS Diff
FROM
CR_MDT a
full outer join CR_MDT_VERIFY b
on a.CR_MDT_ID = b.FK_CR_MDT_ID
left join patient c
on a.FK_SOCRD_ID = c.SOCRD_ID
left join PT_STUDY d
on c.SOCRD_ID = d.FK_SOCRD_ID
where
a.CR_MDT_DT between TO_DATE('01/01/2017', 'mm/dd/yyyy')
and TO_DATE('12/31/2017', 'mm/dd/yyyy')
group by
d.FK_SOCRD_ID,
d.SUBJECT_NUMBER,
c.PTLASTNAME,
c.PTFIRSTNAME,
b.created
How can I subtract the max(b.created) from min(b.created)? I'm stuck on this part:
|| TO_CHAR(to_date('01/01/2000', 'MM-DD-YYYY')
+ (b.created - b.created), 'HH24:MI:SS' ) AS Diff
I need help on how to write the subtraction part since b.created max and min are from the same column.
You can put that query in a subquery and do the subtraction outside:
SELECT *
, max_created - min_created as date_difference
FROM
(
SELECT d.FK_SOCRD_ID
, d.SUBJECT_NUMBER
, c.PTLASTNAME || ', ' || c.PTFIRSTNAME AS "Patient name"
, min(b.created) AS min_created
, max(b.created) AS max_created
FROM CR_MDT a
FULL JOIN CR_MDT_VERIFY b
ON a.CR_MDT_ID = b.FK_CR_MDT_ID
LEFT JOIN patient c
ON c.SOCRD_ID = a.FK_SOCRD_ID
LEFT JOIN PT_STUDY d
ON d.FK_SOCRD_ID = c.SOCRD_ID
WHERE a.CR_MDT_DT BETWEEN TO_DATE('01/01/2017', 'mm/dd/yyyy') AND TO_DATE('12/31/2017', 'mm/dd/yyyy')
GROUP BY d.FK_SOCRD_ID
, d.SUBJECT_NUMBER
, c.PTLASTNAME
, c.PTFIRSTNAME
) subq
You might have to change the datediff function depending on the dbms you are using.
EDIT: I updated the date subtraction part now that i know you use Oracle.
You can use floor(), leave only fractional part of difference and convert it to hh24:mi:ss, like here:
select id, mx, mn, floor(diff) ||' days and '||
to_char(trunc(sysdate) + diff - floor(diff), 'hh24:mi:ss') diff
from (select id, max(created) mx, min(created) mn,
max(created) - min(created) diff
from test group by id)
Test data:
create table test (id number(3), created date);
insert into test values (1, timestamp '2017-09-12 01:00:00');
insert into test values (1, timestamp '2017-11-05 11:24:17');
insert into test values (1, timestamp '2017-12-15 13:42:05');
insert into test values (2, timestamp '2017-01-05 11:00:00');
insert into test values (2, timestamp '2017-06-05 23:12:56');
Result:
ID MX MN DIFF
---- ------------------- ------------------- ---------------------
1 2017-12-15 13:42:05 2017-09-12 01:00:00 94 days and 12:42:05
2 2017-06-05 23:12:56 2017-01-05 11:00:00 151 days and 12:12:56
Here is what gave me the desired result:
SELECT d.FK_SOCRD_ID,
d.SUBJECT_NUMBER,
c.PTLASTNAME || ', ' || c.PTFIRSTNAME AS "Patient name",
min(b.created) AS min_created,
max(b.created) AS max_created,
trunc(max(b.created)) - trunc(min(b.created)) || ' days' AS Diff
FROM CR_MDT a
FULL JOIN CR_MDT_VERIFY b
ON a.CR_MDT_ID = b.FK_CR_MDT_ID
LEFT JOIN patient c
ON c.SOCRD_ID = a.FK_SOCRD_ID
LEFT JOIN PT_STUDY d
ON d.FK_SOCRD_ID = c.SOCRD_ID
WHERE a.CR_MDT_DT BETWEEN TO_DATE('01/01/2017', 'mm/dd/yyyy') AND TO_DATE('12/31/2017', 'mm/dd/yyyy')
GROUP BY d.FK_SOCRD_ID,
d.SUBJECT_NUMBER,
c.PTLASTNAME,
c.PTFIRSTNAME

Find median between 2 dates

Anyone know how I can change the Total Median near bottom to show an average of the median instead? For some reason, the Total Median is always 100. Not sure what I should do.
Thanks in advance for any ideas! Current results also below.
WITH CTE AS (
SELECT DISTINCT c.CaseID AS CaseID,
DATEDIFF(d, c.CaseAddDt, coip.DispoDt) AS DaysApart
, DATEPART(month,c.CaseAddDt) AS [Month]
, DATEPART(year,c.CaseAddDt) AS [Year]
, CAST(DATEPART(year,c.CaseAddDt) AS varchar) + '|' + CASE WHEN DATEPART(month,c.CaseAddDt) IN (10,11,12) THEN CAST(DATEPART(month,c.CaseAddDt) AS varchar) ELSE '0' + CAST(DATEPART(month,c.CaseAddDt) AS varchar) END AS Srt
FROM jw50_Case c
JOIN jw50_CaseInvPers def ON def.CaseID = c.CaseID
AND def.InvolveTypeMasterCode = 1
JOIN
jw50_CountInvPers coip ON coip.CaseID = c.CaseID
AND coip.CaseInvPersID = def.CaseInvPersID
AND coip.DispoCode IN ('CODE','CODE')
AND coip.CountNum > 0
OUTER APPLY (
SELECT TOP 1 caz.CaseAgencyID
FROM jw50_CaseAgency caz
WHERE caz.CaseID = c.CaseID
AND caz.AgencyCode = 'ABC'
AND caz.NumberTypeCode IN ('i#','in#')) caz
WHERE
EXISTS (SELECT 1 FROM jw50_CaseAttributes ca WHERE ca.CaseID = c.CaseID AND ca.CaseAttributeCode = 'oa7')
AND caz.CaseAgencyID IS NOT NULL
AND c.CaseStatusCode <> 'AAA'
AND c.CaseAddDt BETWEEN '01/01/2017' AND '08/01/2017'
AND c.CaseAddDt <= coip.DispoDt)
SELECT a.CaseID,
a.Month
, a.Year
, a.DaysApart
, a.Srt
, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY a.DaysApart) OVER (PARTITION BY a.Month, a.Year) AS MonMedian
, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY a.DaysApart) OVER (PARTITION BY 1) AS TotalMedian
FROM CTE a
Results: