How To select one data from many data (Oracle SQL) - 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;

Related

SQL select distinct from a concatenated column

This query almost does what I want
SELECT staging.dbo.ITEM_CODES.ITEM_CODE, MAX(dbo.OC_VDAT_AUX.UDL40) AS SAMPLEDATE,
CONCAT(RTRIM(dbo.OC_VDATA.UDL1), RTRIM(dbo.OC_VDATA.UDL6)) as LinkID
FROM dbo.OC_VDATA
INNER JOIN dbo.OC_VDAT_AUX ON dbo.OC_VDATA.PARTNO = dbo.OC_VDAT_AUX.PARTNOAUX AND dbo.OC_VDATA.DATETIME = dbo.OC_VDAT_AUX.DATETIMEAUX
INNER JOIN stagingPLM.dbo.ITEM_CODES ON LEFT(dbo.OC_VDATA.PARTNO, 12) = staging.dbo.ITEM_CODES.SPEC_NO
AND LEFT(dbo.OC_VDAT_AUX.PARTNOAUX, 12) = stagingPLM.dbo.ITEM_CODES.SPEC_NO
INNER JOIN stagingPLM.dbo.PLANTS ON dbo.OC_VDATA.UDL1 = staging.dbo.PLANTS.PLANT_CODE
WHERE (CONVERT(DATETIME, dbo.OC_VDAT_AUX.UDL40) > DATEADD(day, - 30, GETDATE()))
GROUP BY CONCAT(RTRIM(dbo.OC_VDATA.UDL1), RTRIM(dbo.OC_VDATA.UDL6)),staging.dbo.ITEM_CODES.ITEM_CODE
Sample Table generated by query:
The end result that I am trying to achieve is the latest ITEM_CODE per unique LinkID Note the first and last rows in the table. The last row should not be pulled by the query.
How do I modify this query to make that happen?
I have tried various placements for DISTINCT and sub queries in the select and where statements.
I would do in your case with ROW_NUMBER window function and CTE.
Solution can be like this:
WITH FilterCTE AS
(
SELECT staging.dbo.ITEM_CODES.ITEM_CODE, MAX(dbo.OC_VDAT_AUX.UDL40) AS SAMPLEDATE,
CONCAT(RTRIM(dbo.OC_VDATA.UDL1), RTRIM(dbo.OC_VDATA.UDL6)) AS LinkID,
ROW_NUMBER() OVER (PARTITION BY CONCAT(RTRIM(dbo.OC_VDATA.UDL1), RTRIM(dbo.OC_VDATA.UDL6)) ORDER BY MAX(dbo.OC_VDAT_AUX.UDL40)) AS RowNumber
FROM dbo.OC_VDATA
INNER JOIN dbo.OC_VDAT_AUX ON dbo.OC_VDATA.PARTNO = dbo.OC_VDAT_AUX.PARTNOAUX AND dbo.OC_VDATA.DATETIME = dbo.OC_VDAT_AUX.DATETIMEAUX
INNER JOIN stagingPLM.dbo.ITEM_CODES ON LEFT(dbo.OC_VDATA.PARTNO, 12) = staging.dbo.ITEM_CODES.SPEC_NO
AND LEFT(dbo.OC_VDAT_AUX.PARTNOAUX, 12) = stagingPLM.dbo.ITEM_CODES.SPEC_NO
INNER JOIN stagingPLM.dbo.PLANTS ON dbo.OC_VDATA.UDL1 = staging.dbo.PLANTS.PLANT_CODE
WHERE (CONVERT(DATETIME, dbo.OC_VDAT_AUX.UDL40) > DATEADD(day, - 30, GETDATE()))
GROUP BY CONCAT(RTRIM(dbo.OC_VDATA.UDL1), RTRIM(dbo.OC_VDATA.UDL6)),staging.dbo.ITEM_CODES.ITEM_CODE
)
SELECT *
FROM FilterCTE
WHERE RowNumber = 1

How to escape several with in the query?

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);

OPTIMIZING QUERY - ORACLE SQL

I have a query fetching information from a large database. Fetching one customer records takes minimum 12 seconds. I have estimated doing through the whole table will require not less than 12 days. Have a look at a it and help if it can be optimized
SELECT gam.CIF_ID
, TO_CHAR(T1.TRAN_DATE, 'MM') AS MONTH
, SUM(TBAADM.COMMONPACKAGE.getConvertedAmount('TZ', tran_amt, tran_crncy_code, 'TZS', 'REV', tran_date)) AS TOTAL_DEBIT_AMT
, ROUND(AVG(TBAADM.COMMONPACKAGE.getConvertedAmount('TZ', tran_amt, tran_crncy_code, 'TZS', 'REV', tran_date)), 3) AS AVG_DEBIT_AMT
, COUNT(T1.PART_TRAN_TYPE) AS NUMBER_OF_DEBIT_TRANSACTIONS
FROM TBAADM.HIST_TRAN_DTL_TABLE T1
LEFT JOIN TBAADM.GENERAL_ACCT_MAST_TABLE gam ON ( gam.ACID = T1.ACID )
WHERE T1.PART_TRAN_TYPE = 'D'
AND NOT EXISTS (
SELECT 1
FROM TBAADM.HIST_TRAN_DTL_TABLE T3
INNER JOIN TBAADM.GENERAL_ACCT_MAST_TABLE g ON ( g.ACID = T3.ACID )
WHERE T3.PART_TRAN_TYPE ='C'
AND T3.TRAN_DATE || T3.TRAN_ID=T1.TRAN_DATE || T1.TRAN_ID
AND g.cif_id = gam.cif_id
)
AND (
T1.TRAN_SUB_TYPE='CI'
OR T1.TRAN_SUB_TYPE='SI'
OR T1.TRAN_SUB_TYPE='PI'
OR T1.TRAN_SUB_TYPE='RI'
)
AND gam.ACCT_OWNERSHIP <> 'O'
AND T1.TRAN_SUB_TYPE <> 'CP'
AND gam.SCHM_TYPE <> 'LAA'
AND tran_particular LIKE 'POS PURCHASE %'
AND T1.TRAN_DATE BETWEEN '1/JAN/14' AND '30/JUNE/18'
GROUP BY TO_CHAR(T1.TRAN_DATE, 'MM')
, gam.CIF_ID
;

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;

How can I change these into columns?

I've tried using CTE, but haven't been able to figure it out. I want to change the multiple rows into one row with multiple columns going accross. Any way to go about this...
Current Result
ern CostPool
111 NULL
111 5
Desired Result
ern CostPool1 CostPool2
111 NULL 5
SELECT first_name, last_name, ern, pc, title, TitleDesc, OfficeTitle, SUM(earned) earned, fypaydate, q
, [RMTS, Support, or Partial Quarter], SUM(CAST([Staff Employment Status] AS INT)) sumEmpStatus, SUM(CAST(countisMHy AS INT)) sumcountisMHy, SUM(CAST(sumisMHy AS INT)) sumisMHy1
, SUM(CAST(sumEligible AS INT)) sumEligible1
, CostPool
FROM (
SELECT
e.first_name, e.last_name
, fss.ern
, fss.pc
, fss4.title
, u.OfficeTitle
, t.TitleDesc
, SUM(fss.gross*fss.[percent]) earned
, fss.fypaydate, fss.q
, cpayfss3, cpaypdq
, CASE WHEN cpayfss3 = cpaypdq THEN '1' --full q
WHEN cpayfss3 < cpaypdq THEN '2' --part q
ELSE 'Check'
END [RMTS, Support, or Partial Quarter]
, CASE WHEN COUNT(DISTINCT rc.isMHy) = 2 THEN '9' --split
WHEN fss.pc = 'A' AND COUNT(DISTINCT rc.isMHy) = 1 AND SUM(CAST(rc.isMHy AS INT)) > 0 THEN '1' --full
WHEN fss.pc IN ('X', 'O', 'W', 'L', 'K') AND SUM(CAST(rc.isMHy AS INT)) > 0 THEN '2' --part
ELSE '0'
END [Staff Employment Status]
, COUNT(DISTINCT rc.isMHy) countisMHy
, SUM(CAST(rc.isMHy AS INT)) sumisMHy
, SUM(CAST(rc.Eligible AS INT)) sumEligible --count distinct too?
, rc.CostPool
FROM [FIN_DataMart].[dbo].[FSSpaydetl_NEW] fss
LEFT JOIN FIN_DataMart.[dbo].[FSSRC_NEW] rc
ON fss.newrc = rc.RC
AND fss.fypaydate = rc.FY
INNER JOIN
(
SELECT ern, fypaydate, q, COUNT(DISTINCT paydate) cpayfss3
FROM [FIN_DataMart].[dbo].[FSSpaydetl_NEW]
GROUP BY fypaydate, q, ern
) fss3
ON fss.ern = fss3.ern
AND fss.fypaydate = fss3.fypaydate
AND fss.q = fss3.q
LEFT JOIN
(
SELECT COUNT(DISTINCT paydate) cpaypdq, fy, q
FROM [FIN_DataMart].[dbo].[FSSPayDateQ]
GROUP BY fy, q
) pdq
ON fss.fypaydate = pdq.fy
AND fss.q = pdq.q
LEFT JOIN
[FIN_DataMart].[dbo].FSSEmployeeInfo e
ON fss.ern = e.ern
INNER JOIN
(
SELECT DISTINCT ern, title, tlvl, fy, q, ROW_NUMBER() OVER (PARTITION BY ern, fy, q ORDER BY title DESC) rn
FROM [FIN_DataMart].[dbo].[FSSpaydetl_NEW]
) fss4
ON fss.ern = fss4.ern
AND fss.fypaydate = fss4.fy
AND fss.q = fss4.q
AND fss4.rn = '1'
LEFT JOIN
(
SELECT TCode, TLevel, GenericTDesc as TitleDesc, EED,
ROW_NUMBER() OVER (PARTITION BY TCode, TLevel ORDER BY EED DESC) rn
FROM [FIN_DataMart].[dbo].FSATitle
) t
ON fss4.title = t.TCode
AND case when fss4.tlvl = '' THEN '01' WHEN fss4.tlvl IS NULL THEN '01' ELSE fss4.tlvl END = t.TLevel
AND t.rn = 1
LEFT JOIN FIN_DataMart.[dbo].[FSSUserERN] u
ON fss.ern = u.ern
WHERE fss.fypaydate = 2015
AND fss.q = 1
AND fss.obj <> '049' AND fss.obj NOT LIKE 'x%'
AND fss.title NOT IN ('10209', '1020B', '10234', '10232', '20202', '21205', '30086', '34201')
GROUP BY
fss.ern
, fss.pc
, fss4.title
, fss.fypaydate, fss.q
, cpayfss3, cpaypdq
, rc.CostPool
, t.TitleDesc
, e.first_name
, e.last_name
, u.OfficeTitle
HAVING SUM(CAST(rc.Eligible AS INT)) >=1
) A
GROUP BY
first_name, last_name, ern, pc, title, fypaydate, q
, [RMTS, Support, or Partial Quarter]
, OfficeTitle
, TitleDesc
, CostPool
ORDER BY first_name, last_name, fypaydate, q
You can look into using the Pivot operator to convert your rows into columns.
https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx
That method would work, although your sample data and expected result do not quite match the massive query you have posted so I cannot provide a more specific example until you provide more test data/expected outputs (and table schema).