Need Help on Oracle Select Query - sql

I am finding difficulty to frame a select query.
PFB, for the table and corresponding data:
ID DLS MATCH_STATUS LAST_UPDATE_TIME BO CH FT
1 0 0 09-07-2013 00:00:00 IT TE NA
1 1 1 09-07-2013 00:01:01 IT TE NA
2 0 0 09-07-2013 10:00:00 IP TE NA
3 0 0 09-07-2013 11:00:00 IT YT NA
3 2 2 09-07-2013 11:01:00 IT YT NA
Here
Match_Status 0-->Initial Record
1-->Singel Match
2-->Multi Match
For every record there will be a initial entry with match_status 0 and subsequent matching process end other number such as 1,2 will be update.
I am trying to retrieve records such as total record , waiting match ,single match and multi match group by BO, CH and FT
Below is the expected out put:
BO CH FT TOTAL_RECORD AWAITNG_MATCH SINGLE_MATCH MULTI_MATCH
IT TE NA 1 0 1 0
IP TE NA 1 1 0 0
IT YT NA 1 0 0 2
I have tried below query :
select BO,CH,FT,sum(case when match_status=0 then 1 else 0 end) as TOTAL_RECORD,
sum(case when match_status = 0 then 1 else 0 end) as AWAITING_MATCH,
sum(case when match_status = 1 then 1 else 0 end) as SINGLE_MATCH,
sum(case when match_status = 2 then 1 else 0 end) as MULTI_MATCH from
table1 where last_update_time >= current_timestamp-1
group by BO,CH,FT;
problem with the above query is, awaiting_match is getting populated same as total record as I understand because of match_status=0
Similarly I tried with
select BO,CH,FT,sum(case when match_status=0 then 1 else 0 end) as TOTAL_RECORD,
select (sum(case when t1.ms=0 then 1 else 0 end) from
(select max(match_status) as ms from table1 where last_update_time >= current_timestamp-1 group by id)t1) )awaiting_match,
sum(case when match_status = 1 then 1 else 0 end) as SINGLE_MATCH,
sum(case when match_status = 2 then 1 else 0 end) as MULTI_MATCH from
table1 where last_update_time >= current_timestamp-1
group by BO,CH,FT;
problem with the approach is awaiting_match is getting populated with the same value for subsequent row entry.
Please help me with a suitable query for the desired format.
Thanks a lot in advance.

It seems that you want the last match status. I am guessing that this is actually the maximum of the statuses. If so, the following solves the problem by first grouping on id and then doing the grouping to summarize:
select BO, CH, FT,
count(*) as TOTAL_RECORD,
sum(case when lastms = 0 then 1 else 0 end) as AWAITING_MATCH,
sum(case when lastms = 1 then 1 else 0 end) as SINGLE_MATCH,
sum(case when lastms = 2 then 1 else 0 end) as MULTI_MATCH
from (select id, bo, ch, ft, MAX(match_status) as lastms
from table1
where last_update_time >= current_timestamp-1
group by id, bo, ch, ft
) t
group by BO, CH, FT;
If you actually want the last update to provide the status for the id, then you can use row_number() to enumerate the rows for each id, order by update time descending, and choose the first one:
select BO, CH, FT,
count(*) as TOTAL_RECORD,
sum(case when lastms = 0 then 1 else 0 end) as AWAITING_MATCH,
sum(case when lastms = 1 then 1 else 0 end) as SINGLE_MATCH,
sum(case when lastms = 2 then 1 else 0 end) as MULTI_MATCH
from (select id, bo, ch, ft, match_status,
ROW_NUMBER() over (partition by id order by last_update_time desc) as seqnum
from table1
where last_update_time >= current_timestamp-1
) t
where seqnum = 1
group by BO, CH, FT;

Related

Adding a dummy identifier to data that varies by position and value

I am working on a project in SQL Server with diagnosis codes and a patient can have up to 4 codes but not necessarily more than 1 and a patient cannot repeat a code more than once. However, codes can occur in any order. My goal is to be able to count how many times a Diagnosis code appears in total, as well as how often it appears in a set position.
My data currently resembles the following:
PtKey
Order #
Order Date
Diagnosis1
Diagnosis2
Diagnosis3
Diagnosis 4
345
1527
7/12/20
J44.9
R26.2
NULL
NULL
367
1679
7/12/20
R26.2
H27.2
G47.34
NULL
325
1700
7/12/20
G47.34
NULL
NULL
NULL
327
1710
7/12/20
I26.2
J44.9
G47.34
NULL
I would think the best approach would be to create a dummy column here that would match up the diagnosis by position. For example, Diagnosis 1 with A, and Diagnosis 2 with B, etc.
My current plan is to rollup the diagnosis using an unpivot:
UNPIVOT ( Diag for ColumnALL IN (Diagnosis1, Diagnosis2, Diagnosis3, Diagnosis4)) as unpvt
However, this still doesn’t provide a way to count the diagnoses by position on a sales order.
I want it to look like this:
Diagnosis
Total Count
Diag1 Count
Diag2 Count
Diag3 Count
Diag4 Count
J44.9
2
1
1
0
0
R26.2
1
1
0
0
0
H27.2
1
0
1
0
0
I26.2
1
1
0
0
0
G47.34
3
1
0
2
0
You can unpivot using apply and aggregate:
select v.diagnosis, count(*) as cnt,
sum(case when pos = 1 then 1 else 0 end) as pos_1,
sum(case when pos = 2 then 1 else 0 end) as pos_2,
sum(case when pos = 3 then 1 else 0 end) as pos_3,
sum(case when pos = 4 then 1 else 0 end) as pos_4
from data d cross apply
(values (diagnosis1, 1),
(diagnosis2, 2),
(diagnosis3, 3),
(diagnosis4, 4)
) v(diagnosis, pos)
where diagnosis is not null;
Another way is to use UNPIVOT to transform the columns into groupable entities:
SELECT Diagnosis, [Total Count] = COUNT(*),
[Diag1 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis1' THEN 1 ELSE 0 END),
[Diag2 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis2' THEN 1 ELSE 0 END),
[Diag3 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis3' THEN 1 ELSE 0 END),
[Diag4 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis4' THEN 1 ELSE 0 END)
FROM
(
SELECT * FROM #x UNPIVOT (Diagnosis FOR DiagGroup IN
([Diagnosis1],[Diagnosis2],[Diagnosis3],[Diagnosis4])) up
) AS x GROUP BY Diagnosis;
Example db<>fiddle
You can also manually unpivot via UNION before doing the conditional aggregation:
SELECT Diagnosis, COUNT(*) As Total Count
, SUM(CASE WHEN Position = 1 THEN 1 ELSE 0 END) As [Diag1 Count]
, SUM(CASE WHEN Position = 2 THEN 1 ELSE 0 END) As [Diag2 Count]
, SUM(CASE WHEN Position = 3 THEN 1 ELSE 0 END) As [Diag3 Count]
, SUM(CASE WHEN Position = 4 THEN 1 ELSE 0 END) As [Diag4 Count]
FROM
(
SELECT PtKey, Diagnosis1 As Diagnosis, 1 As Position
FROM [MyTable]
UNION ALL
SELECT PtKey, Diagnosis2 As Diagnosis, 2 As Position
FROM [MyTable]
WHERE Diagnosis2 IS NOT NULL
UNION ALL
SELECT PtKey, Diagnosis3 As Diagnosis, 3 As Position
FROM [MyTable]
WHERE Diagnosis3 IS NOT NULL
UNION ALL
SELECT PtKey, Diagnosis4 As Diagnosis, 4 As Position
FROM [MyTable]
WHERE Diagnosis4 IS NOT NULL
) d
GROUP BY Diagnosis
Borrowing Aaron's fiddle, to avoid needing to rebuild the schema from scratch, and we get this:
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=d1f7f525e175f0f066dd1749c49cc46d

Creating a view from two tables, with case and sum on one table

I'm having some troubles trying to create a view from two tables, which includes a sum + case for the first table. I've tried multiple different joins/unions, and I can get just the XTS table to come over, or just the case count scenarios to work, but I cannot get both.
here are the tables. For Table 1, UWI is non-unique. For Table 2, UWI is Unique. new_view is what I'm hoping to achieve for my view.
TABLE 1
UWI ET
1 A
1 B
1 B
2 B
2 C
2 C
TABLE 2
UWI XTS
1 10
2 20
3 10
4 30
new_view
UWI XTS B_COUNT C_COUNT
1 10 4 3
2 20 3 4
3 10 4 5
4 30 3 2
Here's what I'm currently working with.
CREATE VIEW new_view AS
SELECT t1.UWI,
sum(case when t1.ET='B' then 1 else 0 end) as B_COUNT,
sum(case when t1.ET='C' then 1 else 0 end) as C_COUNT,
sum(case when t1.ET='D' then 1 else 0 end) as D_COUNT,
sum(case when t1.ET='E' then 1 else 0 end) as E_COUNT,
sum(case when t1.ET='F' then 1 else 0 end) as F_COUNT
FROM TABLE_1 t1
INNER JOIN (SELECT t2.UWI, t2.XTS AS TSC
from TABLE_2 t2)
on t1.UWI = t2.UWI
group by t1.UWI;
Your sample select does not match your sample data, so this is a guess but I think you just need to move the aggregation into an apply()
select t2.UWI, t2.XTS, s.*
from Table_2 t2
outer apply (
select
sum(case when t1.ET='B' then 1 else 0 end) as B_COUNT,
sum(case when t1.ET='C' then 1 else 0 end) as C_COUNT,
sum(case when t1.ET='D' then 1 else 0 end) as D_COUNT,
sum(case when t1.ET='E' then 1 else 0 end) as E_COUNT,
sum(case when t1.ET='F' then 1 else 0 end) as F_COUNT
from table_1 t1
where t1.UWI = t2.UWI
group by t1.UWI
)s

sql subquery that collects from 3 rows

I have a huge database with over 4 million rows that look like that:
Customer ID Shop
1 Asda
1 Sainsbury
1 Tesco
2 TEsco
2 Tesco
I need to count customers that within last 4 weeks had shopped in all 3 shops Tesco Sainsbury and Asda. Can you please advice if its possible to do it with subqueries?
This is an example of a "set-within-sets" subquery. You can solve it with aggregation:
select customer_id
from Yourtable t
where <shopping date within last four weeks>
group by customer_id
having sum(case when shop = 'Asda' then 1 else 0 end) > 0 and
sum(case when shop = 'Sainsbury' then 1 else 0 end) > 0 and
sum(case when shop = 'Tesco' then 1 else 0 end) > 0;
This structure is quite flexible. So if you wanted Asda and Tesco but not Sainsbury, then you would do:
select customer_id
from Yourtable t
where <shopping date within last four weeks>
group by customer_id
having sum(case when shop = 'Asda' then 1 else 0 end) > 0 and
sum(case when shop = 'Sainsbury' then 1 else 0 end) = 0 and
sum(case when shop = 'Tesco' then 1 else 0 end) > 0;
EDIT:
If you want a count, then use this as a subquery and count the results:
select count(*)
from (select customer_id
from Yourtable t
where <shopping date within last four weeks>
group by customer_id
having sum(case when shop = 'Asda' then 1 else 0 end) > 0 and
sum(case when shop = 'Sainsbury' then 1 else 0 end) > 0 and
sum(case when shop = 'Tesco' then 1 else 0 end) > 0
) t

How To Accumulator All The Values

the output i would like to display was accumulator of total EMPL_NUM. For example, the value show in the field TOTAL_FEBRUARY=TOTAL_JANUARY+TOTAL_FEBRUARY,while for the value exist in field TOTAL_MARCH=TOTAL_MARCH+TOTAL_JANUARY+TOTAL_FEBRUARY. I hope some of you can provide the solution for do it. Thank you very much. The coding is show as below:
SELECT
(CASE WHEN To_Char(A.EFFDT,'MM')=01 THEN 1
WHEN To_Char(A.EFFDT,'MM')=02 THEN 2
WHEN To_Char(A.EFFDT,'MM')=03 THEN 3
WHEN To_Char(A.EFFDT,'MM')=04 THEN 4
WHEN To_Char(A.EFFDT,'MM')=05 THEN 5
WHEN To_Char(A.EFFDT,'MM')=06 THEN 6
WHEN To_Char(A.EFFDT,'MM')=07 THEN 7
WHEN To_Char(A.EFFDT,'MM')=08 THEN 8
WHEN To_Char(A.EFFDT,'MM')=09 THEN 9
WHEN To_Char(A.EFFDT,'MM')=10 THEN 10
WHEN To_Char(A.EFFDT,'MM')=11 THEN 11
WHEN To_Char(A.EFFDT,'MM')=12 THEN 12
ELSE NULL END) AS MONTHS
,Count(*) AS EMPL_NUM
,Sum(CASE WHEN To_Char(A.EFFDT,'MM')=01 THEN 1 ELSE 0 END) AS TOTAL_JANUARY
,Sum(CASE WHEN To_Char(A.EFFDT,'MM')=02 THEN 1 ELSE 0 END) AS TOTAL_FEBRUARY
,Sum(CASE WHEN To_Char(A.EFFDT,'MM')=03 THEN 1 ELSE 0 END) AS TOTAL_MARCH
,Sum(CASE WHEN To_Char(A.EFFDT,'MM')=04 THEN 1 ELSE 0 END) AS TOTAL_APRIL
,Sum(CASE WHEN To_Char(A.EFFDT,'MM')=05 THEN 1 ELSE 0 END) AS TOTAL_MAY
,Sum(CASE WHEN To_Char(A.EFFDT,'MM')=06 THEN 1 ELSE 0 END) AS TOTAL_JUN
,Sum(CASE WHEN To_Char(A.EFFDT,'MM')=07 THEN 1 ELSE 0 END) AS TOTAL_JULY
,Sum(CASE WHEN To_Char(A.EFFDT,'MM')=08 THEN 1 ELSE 0 END) AS TOTAL_AUGUST
,Sum(CASE WHEN To_Char(A.EFFDT,'MM')=09 THEN 1 ELSE 0 END) AS TOTAL_SEPTEMBER
,Sum(CASE WHEN To_Char(A.EFFDT,'MM')=10 THEN 1 ELSE 0 END) AS TOTAL_OCTOBER
,Sum(CASE WHEN To_Char(A.EFFDT,'MM')=11 THEN 1 ELSE 0 END) AS TOTAL_NOVEMBER
,Sum(CASE WHEN To_Char(A.EFFDT,'MM')=12 THEN 1 ELSE 0 END) AS TOTAL_DECEMBER
FROM PS_JOB A
,PS_CITIZEN_PSSPRT B
,PS_CITIZENSHIP C
,PS_CITIZEN_STS_TBL D
WHERE A.HR_STATUS='A'
AND A.EFFDT=(SELECT Max(A1.EFFDT) FROM PS_JOB A1 WHERE A.EMPLID=A1.EMPLID AND A.EMPL_RCD=A1.EMPL_RCD AND A1.EFFDT<=SYSDATE)
AND A.EMPL_RCD=0
AND A.EFFSEQ=(SELECT Max(A2.EFFSEQ) FROM PS_JOB A2 WHERE A.EMPLID=A2.EMPLID AND A.EMPL_RCD=A2.EMPL_RCD AND A.EFFDT=A2.EFFDT)
AND A.EMPLID =B.EMPLID(+)
AND B.DEPENDENT_ID=' '
AND A.EMPLID=C.EMPLID
AND B.EMPLID=C.EMPLID
AND B.DEPENDENT_ID=C.DEPENDENT_ID
AND B.COUNTRY=C.COUNTRY
AND B.COUNTRY=D.COUNTRY
AND C.COUNTRY=D.COUNTRY
AND C.CITIZENSHIP_STATUS=D.CITIZENSHIP_STATUS
AND C.CITIZENSHIP_STATUS IN ('5','7')
AND To_Char(A.EFFDT,'YYYY')=2012
GROUP BY CASE WHEN To_Char(A.EFFDT,'MM')=01 THEN 1
WHEN To_Char(A.EFFDT,'MM')=02 THEN 2
WHEN To_Char(A.EFFDT,'MM')=03 THEN 3
WHEN To_Char(A.EFFDT,'MM')=04 THEN 4
WHEN To_Char(A.EFFDT,'MM')=05 THEN 5
WHEN To_Char(A.EFFDT,'MM')=06 THEN 6
WHEN To_Char(A.EFFDT,'MM')=07 THEN 7
WHEN To_Char(A.EFFDT,'MM')=08 THEN 8
WHEN To_Char(A.EFFDT,'MM')=09 THEN 9
WHEN To_Char(A.EFFDT,'MM')=10 THEN 10
WHEN To_Char(A.EFFDT,'MM')=11 THEN 11
WHEN To_Char(A.EFFDT,'MM')=12 THEN 12
ELSE NULL END
nter code here
Use <= operator when calculating SUM, e.g.:
Sum(CASE WHEN TO_NUMBER(To_Char(A.EFFDT,'MM')) <= 3 THEN 1 ELSE 0 END) AS TOTAL_MARCH
I think what you are asking for is a running total. Take a look at analytic functions (for example, the psoug site). If the core of your query is correct, do a regular aggregate in the inner query, then wrap that in an analytic function for the running total.
SELECT month
,SUM(month_count) OVER ( ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW )
AS running_total
FROM ( SELECT To_Char(A.EFFDT,'MM') AS month
,COUNT(*) AS month_count
FROM PS_JOB A
,PS_CITIZEN_PSSPRT B
,PS_CITIZENSHIP C
,PS_CITIZEN_STS_TBL D
WHERE A.HR_STATUS='A'
AND A.EFFDT=( SELECT Max(A1.EFFDT)
FROM PS_JOB A1
WHERE A.EMPLID=A1.EMPLID
AND A.EMPL_RCD=A1.EMPL_RCD
AND A1.EFFDT<=SYSDATE )
AND A.EMPL_RCD=0
AND A.EFFSEQ=( SELECT Max(A2.EFFSEQ)
FROM PS_JOB A2
WHERE A.EMPLID=A2.EMPLID
AND A.EMPL_RCD=A2.EMPL_RCD
AND A.EFFDT=A2.EFFDT )
AND A.EMPLID =B.EMPLID(+)
AND B.DEPENDENT_ID=' '
AND A.EMPLID=C.EMPLID
AND B.EMPLID=C.EMPLID
AND B.DEPENDENT_ID=C.DEPENDENT_ID
AND B.COUNTRY=C.COUNTRY
AND B.COUNTRY=D.COUNTRY
AND C.COUNTRY=D.COUNTRY
AND C.CITIZENSHIP_STATUS=D.CITIZENSHIP_STATUS
AND C.CITIZENSHIP_STATUS IN ('5','7')
AND To_Char(A.EFFDT,'YYYY')=2012
GROUP BY To_Char(A.EFFDT,'MM')
)
ORDER BY month

Full Join on two queries

I'm trying to do a full join on two SQL queries, below:
1st Query:
SELECT
ID
,SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END) AS TValue
,COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate
GROUP BY ID
2nd Query:
SELECT
ID
,SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END) AS New
,SUM(CASE WHEN reason = 6 THEN 1 ELSE 0 END) AS Amend
,SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2
WHERE Date2 >=#StartDate AND Date2<= #EndDate
GROUP BY ID
Result from query1
ID CountAll TValue
-------------------------
id1 24 1020
id2 13 2030
id3 4 120
Result from query 2:
ID New Amend Cancel
--------------------------------
id1 12 4 6
id2 7 6 1
id4 2 1 2
Needed output:
ID TValue CountAll New Amend Cancel Total(countall+new+amend+cancel)
----------------------------------------------------------------------------------------
Id1 1020 24 12 4 6 46
Id2 2030 13 7 6 1 27
id3 120 4 0 0 0 4
Id4 0 0 2 1 2 5
I'll post my current solution if requested, but it is pretty far from working.
I've been doing a bit of research and I think I need to either make a union to join the ID'S, or just do a Full Join. (Second day ever doing sql)
Try this,
SELECT *
FROM
(
SELECT ID ,
SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END) AS TValue,
COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate
GROUP BY ID
) a FULL JOIN
(
SELECT ID ,
SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END) AS New ,
SUM(CASE WHEN reason = 6 THEN 1 ELSE 0 END) AS Amend ,
SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2
WHERE Date2 >=#StartDate AND Date2<= #EndDate
GROUP BY ID
) b ON a.ID = b.ID
I would write something like below:
select decode (a.id, null, b.id, a.id) as ID, a.TValue, CountAll, b.new, b.Amend, b.cancel
from (SELECT ID ,SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END)
AS TValue ,COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate GROUP BY ID
) a FULL OUTER JOIN
(SELECT ID , SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END)
AS New ,SUM(CASE WHEN reason = 6
THEN 1 ELSE 0 END) AS Amend ,
SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2 WHERE Date2 >=#StartDate AND Date2<= #EndDate GROUP BY ID
) b
on a.id = b.id
have you tried this...
select isnull (a.id,b.id) as ID, a.TValue, CountAll, b.new, b.Amend, b.cancel
from (SELECT ID ,SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END) AS TValue ,COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate GROUP BY ID ) a
FULL OUTER JOIN (SELECT ID , SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END) AS New ,SUM(CASE WHEN reason = 6 THEN 1 ELSE 0 END) AS Amend , SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2 WHERE Date2 >=#StartDate AND Date2<= #EndDate GROUP BY ID ) b on a.id = b.id