How to improve Using RANK() top 2 results into unique columns - sql

Here is the SQL 9(i) code written to show results 1 and 2 in their own columns. Is there a more efficient way to write this?
select
sc1.COIL as COIL1
, sc1.DEFECT as DEFECT1
, sc2.DEFECT as DEFECT2
FROM
(select
COIL, DEFECT
, RANK() OVER(PARTITION BY COIL ORDER BY WEIGHT DESC) RNK
from NOVELIS.F406, NOVELIS.F408 where F406_DEFECT_CODE = F408_REJECT_CODE
GROUP BY COIL, DEFECT
)sc1
, (select
COIL, DEFECT
, RANK() OVER(PARTITION BY COIL ORDER BY WEIGHT DESC) RNK
from NOVELIS.F406, NOVELIS.F408 where F406_DEFECT_CODE = F408_REJECT_CODE
GROUP BY COIL, DEFECT
)sc2
WHERE
sc1.RNK = 1
and sc2.RNK = 2
and sc1.COIL = sc2.COIL

You can use conditional aggregation and use 1 derived table
SELECT sc1.COIL AS COIL1,
MAX(CASE WHEN RNK = 1 THEN sc1.DEFECT END) AS DEFECT1,
MAX(CASE WHEN RNK = 2 THEN sc1.DEFECT END) AS DEFECT2
FROM
(
SELECT COIL,
DEFECT,
RANK() OVER(PARTITION BY COIL ORDER BY WEIGHT DESC) RNK
FROM NOVELIS.F406
--practice using joins
INNER JOIN NOVELIS.F408 ON F406_DEFECT_CODE = F408_REJECT_CODE
--not sure you need the group by here
) sc1
GROUP BY sc1.COIL

To avoid reading the tables twice, just read them once and pivot out the results:-
select
coil,
max (case when rnk=1 then defect else null end) defect1,
max (case when rnk=2 then defect else null end) defect2
FROM (
select
COIL, DEFECT,
RANK() OVER(PARTITION BY COIL ORDER BY WEIGHT DESC) RNK
from NOVELIS.F406
inner join NOVELIS.F408 on F406_DEFECT_CODE = F408_REJECT_CODE
) sc
WHERE
sc.RNK <= 2
group by coil

Related

Displaying a multiple columns in single row in SQL

SELECT * FROM
(select ID,POLICY_ID,CONTACT_ID,POLICY_CONTACT_ROLE,ROW_NUMBER () OVER (PARTITION BY POLICY_ID,POLICY_CONTACT_ROLE ORDER BY ID ) AS ORDERNO
FROM P_POL_HEADER_CONTACT ) SUB
WHERE SUB.ORDERNO=1 AND POLICY_ID =20001
I want to arrange the above query to take result like this
Thanks
You need to pivot on a row-number.
You can use PIVOT, but I often find a simple GROUP BY/MAX is simpler, especially when you need to rename the columns.
SELECT
SUB.POLICY_ID
,MAX(CASE WHEN POLICY_CONTACT_ROLE_NO = 1 THEN POLICY_CONTACT_ROLE END) AS POLICY_CONTACT_ROLE
,MAX(CASE WHEN POLICY_CONTACT_ROLE_NO = 2 THEN POLICY_CONTACT_ROLE END) AS POLICY_CONTACT_ROLE2
,MAX(CASE WHEN POLICY_CONTACT_ROLE_NO = 3 THEN POLICY_CONTACT_ROLE END) AS POLICY_CONTACT_ROLE3
-- etc etc
FROM (
SELECT *,
ROW_NUMBER () OVER (PARTITION BY POLICY_ID ORDER BY POLICY_CONTACT_ROLE) AS POLICY_CONTACT_ROLE_NO
FROM (
SELECT *
ROW_NUMBER () OVER (PARTITION BY POLICY_ID, POLICY_CONTACT_ROLE ORDER BY ID ) AS ORDERNO
FROM P_POL_HEADER_CONTACT
WHERE POLICY_ID = 20001
) SUB
WHERE SUB.ORDERNO = 1
) SUB
GROUP BY SUB.POLICY_ID
You want conditional aggregation. I think the logic you want is:
SELECT PCH.POLICY_ID,
MAX(CASE WHEN SEQNUM = 1 THEN POLICY_CONTACT_ROLE END) as POLICY_CONTACT_ROLE_1,
MAX(CASE WHEN SEQNUM = 2 THEN POLICY_CONTACT_ROLE END) as POLICY_CONTACT_ROLE_2,
MAX(CASE WHEN SEQNUM = 3 THEN POLICY_CONTACT_ROLE END) as POLICY_CONTACT_ROLE_3
FROM (SELECT PHC.*,
ROW_NUMBER() OVER (PARTITION BY POLICY_ID ORDER BY ID ) AS SEQNUM
FROM P_POL_HEADER_CONTACT PHC
) PHC
WHERE POLICY_ID = 20001
GROUP BY POLICY_ID;
I'm not sure what your filtering on ORDERNO is really doing. If you have duplicates, use DENSE_RANK() instead of ROW_NUMBER(). However, without knowing what your data looks like, I am guessing that is unnecessary.
Exclude CONTACT_ID from query. And use STUFF query to convert your rows into columns

(HANA SQL) Show multiple values in one row

I am trying to complete the following:
Old situation
What I want
For a fixed maximum number of target columns, you can use window functions and conditional aggregation:
select customer,
max(case when rn = 1 then order_date end) as order_date_1,
max(case when rn = 2 then order_date end) as order_date_2,
max(case when rn = 3 then order_date end) as order_date_3
from (
select t.*, row_number() over(partition by customer order by order_date) rn
from mytable t
) t
group by customer

Split Record into multiple column after Row_number and Partition By

I want to have my result to be displayed in 4 columns.
This is how I did my coding.
SELECT T.MT_CARD_TYP_ID,
ROW_NUMBER() OVER (PARTITION BY T.APP_ID ORDER BY T.DT_CREATE) AS RN
FROM T_CS_FAC_CC T
WHERE app_id = '8F9A97B0CB5349429C44F15830EDC18F';
What should I do the next step? Can someone pro help me out please?
This is how my result look
This is how I want the result look like.
You can try to use condition aggravated function.
SELECT app_id,
MAX(CASE WHEN RN = 1 THEN MT_CARD_TYP_ID END),
MAX(CASE WHEN RN = 3 THEN MT_CARD_TYP_ID END),
MAX(CASE WHEN RN = 2 THEN MT_CARD_TYP_ID END),
MAX(CASE WHEN RN = 4 THEN MT_CARD_TYP_ID END)
FROM (
SELECT T.MT_CARD_TYP_ID, ROW_NUMBER() OVER(PARTITION BY T.APP_ID ORDER BY T.DT_CREATE) AS RN
FROM T_CS_FAC_CC T
where app_id='8F9A97B0CB5349429C44F15830EDC18F'
)t1
GROUP BY app_id

Sql query with join and group by and

I have two table in sql. First one the patient list, second one is their report. all patient's reports are in the report, just with id we can join them. Each patient has some reports (Maybe all the fields of a record is not filled). Now I want to make a report that get the last report of each patient but if some field are empty in the last record of that patient I should fill it with last filled record of that patients records. I have date in the table of reports.
I want to do it for all patients. Here I will add a pic for one patient as an example
In the example above, I want just highlighted ones for this patient in the report.
I have write this query but it give even when a filed in the last record is null while it has data in previous records.
SELECT patient.bartar_id,patient.bartar_enteringthesystem,patient.bartar_proviencename,
patient.bartar_cityname,patient.bartar_coloplastrepname,patient.bartar_consultorname,
patient.bartar_provienceofsurgeryname,patient.bartar_cityofsurgeryname,
patient.bartar_surgeryhospitalname,patient.bartar_doctor,patient.bartar_patientstatusname,
patient.bartar_ostomytypename, patient.bartar_ostomytimename,
r.bartar_date,r.bartar_delay,r.bartar_nextcall,r.new_newcaller,
r.bartar_brandname,r.bartar_pastename,r.bartar_bagname,r.bartar_accname,
r.bartar_pastepermonth,r.bartar_bagepermonth,r.bartar_insuranceinfo,
patient.bartar_deathhealeddate,patient.bartar_dateofseurgery
FROM [Bartar_MSCRM].[dbo].[Filteredbartar_newpaitient] as patient
JOIN (SELECT r.*, row_number() over (partition by r.bartar_patientname
order by r.bartar_date desc) as seqnum
FROM [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] as r
where r.bartar_delay is not null
) r
ON r.bartar_patientname = patient.bartar_newpaitientid and seqnum = 1
ORDER BY patient.bartar_id DESC ;
patient Table
Report Table
Join
Final Report What I want
this is a sample,
in your case you have to get the value of each column in a subquery (either in the join statement, or in the main select statement
example:
inner join (
select distinct bartar_patientname
,(select top 1 bartar_pastePerMonth from [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] c2 where c2.bartar_patientname = cte.bartar_patientname and c2.bartar_pastePerMonth is not null order by c2.bartar_date desc) as bartar_date
,(select top 1 bartar_acc from [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] c2 where c2.bartar_patientname = cte.bartar_patientname and c2.bartar_acc is not null order by c2.bartar_date desc) as bartar_acc
,(select top 1 bartar_insuranceinfo from [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] c2 where c2.bartar_patientname = cte.bartar_patientname and c2.bartar_insuranceinfo is not null order by c2.bartar_date desc) as bartar_insuranceinfo
,(select top 1 bartar_brand from [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] c2 where c2.bartar_patientname = cte.bartar_patientname and c2.bartar_brand is not null order by c2.bartar_date desc) as bartar_brand
from [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] cte
) r
Again, this is a sample of the solution.
Your script is fine as it looks, so I'll just place that on a temporary table for now, and do a per sequence query and filter it by "OR" afterwards.
Please try the script below.
SELECT patient.bartar_id,patient.bartar_enteringthesystem,patient.bartar_proviencename,
patient.bartar_cityname,patient.bartar_coloplastrepname,patient.bartar_consultorname,
patient.bartar_provienceofsurgeryname,patient.bartar_cityofsurgeryname,
patient.bartar_surgeryhospitalname,patient.bartar_doctor,patient.bartar_patientstatusname,
patient.bartar_ostomytypename, patient.bartar_ostomytimename,
r.bartar_date,r.bartar_delay,r.bartar_nextcall,r.new_newcaller,
r.bartar_brandname,r.bartar_pastename,r.bartar_bagname,r.bartar_accname,
r.bartar_pastepermonth,r.bartar_bagepermonth,r.bartar_insuranceinfo,
patient.bartar_deathhealeddate,patient.bartar_dateofseurgery
, ROW_NUMBER() OVER (PARTITION BY r.bartar_newpaitientid, r.bartar_pastepermonth ORDER BY r.bartar_date DESC) AS bartarpaste_sequence
, ROW_NUMBER() OVER (PARTITION BY r.bartar_newpaitientid, r.bartar_acc ORDER BY r.bartar_date DESC) AS bartaracc_sequence
, ROW_NUMBER() OVER (PARTITION BY r.bartar_newpaitientid, r.bartar_insuranceinfo ORDER BY r.bartar_date DESC) AS bartarins_sequence
, ROW_NUMBER() OVER (PARTITION BY r.bartar_newpaitientid, r.bartar_brandname ORDER BY r.bartar_date DESC) AS bartarbrd_sequence
INTO #tmpPatientReport
FROM [Bartar_MSCRM].[dbo].[Filteredbartar_newpaitient] as patient
JOIN (SELECT r.*, row_number() over (partition by r.bartar_patientname
order by r.bartar_date desc) as seqnum
FROM [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] as r
where r.bartar_delay is not null
) r
ON r.bartar_patientname = patient.bartar_newpaitientid and seqnum = 1
ORDER BY patient.bartar_id DESC;
SELECT
*
FROM #tmpPatientReport
WHERE
bartarpaste_sequence = 1
OR bartaracc_sequence = 1
OR bartarins_sequence = 1
OR bartarbrd_sequence = 1
just do join for each column
JOIN (SELECT r.colx, row_number() over (partition by r.bartar_patientname
order by r.bartar_date desc) as seqnum
FROM [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] as r
where r.bartar_delay is not null and r.colx in not null
) rx
ON rx.bartar_patientname = patient.bartar_newpaitientid and seqnum = 1

Find minimum value in groups of rows

In the SQL space (specifically T-SQL, SQL Server 2008), given this list of values:
Status Date
------ -----------------------
ACT 2012-01-07 11:51:06.060
ACT 2012-01-07 11:51:07.920
ACT 2012-01-08 04:13:29.140
NOS 2012-01-09 04:29:16.873
ACT 2012-01-21 12:39:37.607 <-- THIS
ACT 2012-01-21 12:40:03.840
ACT 2012-05-02 16:27:17.370
GRAD 2012-05-19 13:30:02.503
GRAD 2013-09-03 22:58:48.750
Generated from this query:
SELECT Status, Date
FROM Account_History
WHERE AccountNumber = '1234'
ORDER BY Date
The status for this particular object started at ACT, then changed to NOS, then back to ACT, then to GRAD.
What is the best way to get the minimum date from the latest "group" of records where Status = 'ACT'?
Here is a query that does this, by identifying the groups where the student statuses are the same and then using simple aggregation:
select top 1 StudentStatus, min(WhenLastChanged) as WhenLastChanged
from (SELECT StudentStatus, WhenLastChanged,
(row_number() over (order by "date") -
row_number() over (partition by studentstatus order by "date)
) as grp
FROM Account_History
WHERE AccountNumber = '1234'
) t
where StudentStatus = 'ACT'
group by StudentStatus, grp
order by WhenLastChanged desc;
The row_number() function assigns sequential numbers within groups of rows based on the date. For your data, the two row_numbers() and their difference is:
Status Date
------ -----------------------
ACT 2012-01-07 11:51:06.060 1 1 0
ACT 2012-01-07 11:51:07.920 2 2 0
ACT 2012-01-08 04:13:29.140 3 3 0
NOS 2012-01-09 04:29:16.873 4 1 3
ACT 2012-01-21 12:39:37.607 5 4 1
ACT 2012-01-21 12:40:03.840 6 5 1
ACT 2012-05-02 16:27:17.370 7 6 1
GRAD 2012-05-19 13:30:02.503 8 1 7
GRAD 2013-09-03 22:58:48.750 9 2 7
Notice the last row is constant for rows that have the same status.
The aggregation brings these together and chooses the latest (top 1 . . . order by date desc) of the first dates (min(date)).
EDIT:
The query is easy to tweak for multiple account numbers. I probably should have written that way to begin with, except the final selection is trickier. The results from this has the date for each status and account:
select StudentStatus, min(WhenLastChanged) as WhenLastChanged
from (SELECT StudentStatus, WhenLastChanged, AccountNumber
(row_number() over (partition by AccountNumber order by WhenLastChanged) -
row_number() over (partition by AccountNumber, studentstatus order by WhenLastChanged)
) as grp
FROM Account_History
) t
where StudentStatus = 'ACT'
group by AccountNumber, StudentStatus, grp
order by WhenLastChanged desc;
But you can't get the last one per account quite so easily. Another level of subqueries:
select AccountNumber, StudentStatus, WhenLastChanged
from (select AccountNumber, StudentStatus, min(WhenLastChanged) as WhenLastChanged,
row_number() over (partition by AccountNumber, StudentStatus order by min(WhenLastChanged) desc
) as seqnum
from (SELECT AccountNumber, StudentStatus, WhenLastChanged,
(row_number() over (partition by AccountNumber order by WhenLastChanged) -
row_number() over (partition by AccountNumber, studentstatus order by WhenLastChanged)
) as grp
FROM Account_History
) t
where StudentStatus = 'ACT'
group by AccountNumber, StudentStatus, grp
) t
where seqnum = 1;
This uses aggregation along with the window function row_number(). This is assigning sequential numbers to the groups (after aggregation), with the last date for each account getting a value of 1 (order by min(WhenLastChanged) desc). The outermost select then just chooses that row for each account.
SELECT [Status], MIN([Date])
FROM Table_Name
WHERE [Status] = (SELECT [Status]
FROM Table_Name
WHERE [Date] = (SELECT MAX([Date])
FROM Table_Name)
)
GROUP BY [Status]
Try here Sql Fiddle
Hogan: basically, yes. I just want to know the date/time when the
account was last changed to ACT. The records after the point above
marked THIS are just extra.
Instead of just looking for act we can look for first time status changes and select act (and max) from that.
so... every time a status changes:
with rownumb as
(
select *, row_number() OVER (order by date asc) as rn
)
select status, date
from rownumb A
join rownumb B on A.rn = B.rn-1
where a.status != b.status
now finding the max of the act items.
with rownumb as
(
select *, row_number() OVER (order by date asc) as rn
), statuschange as
(
select status, date
from rownumb A
join rownumb B on A.rn = B.rn-1
where a.status != b.status
)
select max(date)
from satuschange
where status='Act'