database system - sql

I need help in calculating the length of days based on Discharge_date and admission_date for all the admission_id. thanks
ADMISSION_ID PATIENT_ID ADMISSION_ EXP ADMITTED_BY WAR DISCHARGE_
------------ ---------- ---------- --- ----------- --- ----------
205 101 02/02/2011 HB 114 P 16/02/2011
275 101 01/09/2010 HY 115 L 01/11/2010
286 101 03/05/2016 AR 116 A 03/07/2016
303 101 03/04/2018 LA 125 F 13/05/2018
298 103 23/01/2016 TS 114 L 24/04/2016
299 103 23/03/2016 AP 114 L 23/04/2016
305 103 23/04/2018 HT 125 F 29/05/2018
321 103 13/05/2018 AR 125 F 23/05/2018
283 105 03/12/2015 AR 116 A 05/12/2015
278 105 01/01/2011 HB 115 P 30/01/2011
307 105 03/04/2018 TS 125 F 13/05/2018

In Oracle, you can simply subtract two dates and get the difference in days.
Similar question: DATEDIFF function in Oracle

Related

How to remove duplicates based on condition?

Here is my sample table:
idmain
idtime
idperson1
idperson2
141
20220106
510
384
221
20220107
300
184
221
20220107
301
184
465
20220108
300
184
525
20220109
111
123
525
20220109
112
123
525
20220109
113
123
Duplicated records only differ by idperson1. So I require to remove these records preserving only the record with the max value of idperson1. So my final table should be:
idmain
idtime
idperson1
idperson2
141
20220106
510
384
221
20220107
301
184
465
20220108
300
184
525
20220109
113
123
db<>fiddle
first you can use subquery to obtain max value of idperson1.
then use this condition like this:
select a.* from fact1 a
where idperson1=(select max(b.idperson1) from fact1 b where a.idtime=b.idtime and a.idperson2=b.idperson2);

SQL to find related rows in Loop in ANSI SQL or Snowflake SQL

I have a requirement where I need to link all related CUSTOMER ID and assign a Unified Cust ID to all the related Cust_id.
Ex: for below data,
INPUT DATA
PK_ID CUST_ID_1 CUST_ID_2 CUST_ID_3
1 123 456 567
2 898 567 780
3 999 780 111
4 111 222 333
Based on CUST_ID_1/CUST_ID_2/CUST_ID_3 need to link all the and assign a Unified ID to all the rows.
OUTPUT DATA
Unified ID CUST_ID_1 CUST_ID_2 CUST_ID_3
1000 123 456 567
1000 898 567 780
1000 999 780 111
1000 111 222 333
Trying to perform Self Join but it cannot be definite. Is there a function or ANSI SQL feature which can help in this?
What i have tried,
CREATE TEMP TBL_TEMP AS(
SELECT A.PK_ID
FROM TBL A
LEFT JOIN TBL B
ON A.CUST_ID_1=B.CUST_ID_1
AND A.PK_ID<>B.PK_ID)
UPDATE TBL
FROM TBL_TEMP
SET UNIFIED_ID=SEQ_UNIF_ID.nextval
WHERE TBL.PK_ID=TBL_TEMP.PK_ID
This update i have to write for each column and multiple times.
If you are ok with gap in sequences then following is what I can come up with as of now.
update cust_temp a
set unified_id = t.unified_id
from
(
select
case
when (select count(*) from cust_temp b
where arrays_overlap(array_construct(a.cust_id_1,a.cust_id_2,a.cust_id_3),
array_construct(b.cust_id_1,b.cust_id_2,b.cust_id_3)))>1 -- match across data-set
then 1000 -- same value for common rows
else
ts.nextval --- using sequence for non-common rows
end unified_id,
a.cust_id_1,a.cust_id_2,a.cust_id_3
from cust_temp a, table(getnextval(SEQ_UNIF_ID)) ts) t
where t.cust_id_1 = a.cust_id_1
and t.cust_id_2 = a.cust_id_2
and t.cust_id_3 = a.cust_id_3;
Updated data-set
select * from cust_temp;
UNIFIED_ID
CUST_ID_1
CUST_ID_2
CUST_ID_3
1000
123
456
567
1000
898
567
780
1000
111
222
333
20000
100
200
300
1000
999
780
111
1000
234
123
901
23000
260
360
460
24000
160
560
760
Original data set -
select * from cust_temp;
UNIFIED_ID
CUST_ID_1
CUST_ID_2
CUST_ID_3
NULL
123
456
567
NULL
898
567
780
NULL
111
222
333
NULL
100
200
300
NULL
999
780
111
NULL
234
123
901
NULL
260
360
460
NULL
160
560
760
Arrays_overlap logic is thanks to #Simeon.
Following procedure can be used -
EXECUTE IMMEDIATE $$
DECLARE
duplicate number;
x number;
BEGIN
duplicate := (select count(cnt) from (select a.unified_id,count(*) cnt from cust_temp a,
cust_temp b
where
arrays_overlap(array_construct(a.cust_id_1,a.cust_id_2,a.cust_id_3),
array_construct(b.cust_id_1,b.cust_id_2,b.cust_id_3))
AND a.cust_id_1 != b.cust_id_1
AND a.cust_id_2 != b.cust_id_2
AND a.cust_id_3 != b.cust_id_3
group by a.unified_id) where cnt>1
);
for x in 1 to duplicate do
update cust_temp a
set a.unified_id = (select min(b.unified_id) uid from cust_temp b
where arrays_overlap(array_construct(a.cust_id_1,a.cust_id_2,a.cust_id_3),
array_construct(b.cust_id_1,b.cust_id_2,b.cust_id_3)));
end for;
END;
$$
;
Which will produce following output dataset -
UNIFIED_ID
CUST_ID_1
CUST_ID_2
CUST_ID_3
1000
100
200
300
2000
123
456
567
2000
898
567
780
2000
111
222
333
2000
999
780
111
2000
234
123
901
7000
260
360
460
8000
160
560
760
8000
186
160
766
For an input data-set as -
UNIFIED_ID
CUST_ID_1
CUST_ID_2
CUST_ID_3
1000
100
200
300
2000
123
456
567
3000
898
567
780
4000
111
222
333
5000
999
780
111
6000
234
123
901
7000
260
360
460
8000
160
560
760
9000
186
160
766

How to use UNPIVOT in SQL Server

I have a table StoreDetails with the following data.
Store 1 2 3
-------------------------------
101 138 282 220
102 96 212 123
105 37 78 60
109 59 97 87
My required output is:
Store Week xCount
---------------------------------
101 1 138
102 1 96
105 1 37
109 1 59
101 2 282
102 2 212
105 2 78
109 2 97
101 3 220
102 3 123
105 3 60
109 3 87
How can I get this result set using UNPIVOT?
I prefer using cross apply:
select sd.store, v.week, v.xcount
from storedetails sd cross apply
(values (1, sd.[1]), (2, sd.[2]), (3, sd.[3]), (4, sd.[4])
) v(week, xcount);
Why do I prefer apply over unpivot? unpivot is very specific syntax that does only one thing (and is specific to SQL Server and Oracle).
On the other hand, apply is an introduction to lateral joins. These are very powerful and unpivoting is just one thing that they can do.
you can try below-
select store,week, xcount
from StoreDetails
unpivot
(
xcount for week in (1,2,3)
)up

Trying to do sum math using Partition By and Row_Number

I'm trying to add a few columns to my table and I'm a bit of the way there but not clear why it's failing. This is an example starting table...
Date Name Amount
1/2/2015 Andy 148
2/5/2015 Andy 188
2/11/2015 Andy 154
1/15/2015 John 136
2/5/2015 John 176
1/7/2015 John 134
1/19/2015 John 251
2/21/2015 Carlos 120
2/15/2015 Carlos 211
1/8/2015 Carlos 120
1/2/2014 Andy 151
2/5/2014 Andy 281
2/11/2014 Andy 298
1/15/2014 John 292
2/5/2014 John 134
1/7/2014 John 281
1/19/2014 John 101
2/21/2014 Carlos 137
2/15/2014 Carlos 108
1/8/2014 Carlos 292
I want to take the above table and...
1) Sort by Year, Name, Then Value
2) Based on #1, Add the "Ordered" column which gives a number for each set of Year and Name where Value is set sorted ascending
3) Multiplied column is multiplying Amount by Ordered
4) Sum the multiplied column and a the sum to each set
Result...
Date Year Name Amount Ordered Multiplied Sum
1/2/2014 2014 Andy 151 1 151 1607
2/5/2014 2014 Andy 281 2 562 1607
2/11/2014 2014 Andy 298 3 894 1607
2/15/2014 2014 Carlos 108 1 108 1258
2/21/2014 2014 Carlos 137 2 274 1258
1/8/2014 2014 Carlos 292 3 876 1258
1/19/2014 2014 John 101 1 101 2380
2/5/2014 2014 John 134 2 268 2380
1/7/2014 2014 John 281 3 843 2380
1/15/2014 2014 John 292 4 1168 2380
1/2/2015 2015 Andy 148 1 148 1020
2/11/2015 2015 Andy 154 2 308 1020
2/5/2015 2015 Andy 188 3 564 1020
1/8/2015 2015 Carlos 120 1 120 993
2/21/2015 2015 Carlos 120 2 240 993
2/15/2015 2015 Carlos 211 3 633 993
1/7/2015 2015 John 134 1 134 1938
1/15/2015 2015 John 136 2 272 1938
2/5/2015 2015 John 176 3 528 1938
1/19/2015 2015 John 251 4 1004 1938
I have everything but the last column as I keep getting the error...
'Invalid expression near Row_Number'.
SQL for 'Ordered'...
ROW_NUMBER() OVER ( Partition BY Name, DATEPART(YEAR, Date) ORDER BY Amount ) AS 'Ordered'
SQL for 'Multiplied'...
Amount * Ordered AS Multiplied
Now I could be thinking of this naively but I thought I could just do add a line like this...
sum(Multiplied) OVER ( Partition BY Name, DATEPART(YEAR, Date) ORDER BY Amount ) AS 'Sum'
But I keep getting the error mentioned. Any ideas how to handle? I'm welcome to hearing other ways of handling the data. I only care about the last column
If your syntax worked, it would produce a cumulative sum. That doesn't appear to be what you want.
I think you can do what you want with a subquery:
select t.*,
(seqnum * amount) as multiplied,
sum(seqnum * amount) over (partition by name, year(date)) as thesum
from (select t.*,
row_number() over (partition by name, year(date) order by date) as seqnum
from table t
) t;

SQL query self join

I am working on a query for a report in Oracle 10g.
I need to generate a short list of each course along with the number of times they were offered in the past year (including ones that weren't actually offered).
I created one query
SELECT coursenumber, count(datestart) AS Offered
FROM class
WHERE datestart BETWEEN (sysdate-365) AND sysdate
GROUP BY coursenumber;
Which produces
COURSENUMBER OFFERED
---- ----------
ST03 2
PD01 1
AY03 2
TB01 4
This query is all correct. However ideally I want it to list those along with COURSENUMBER HY and CS in the left column as well with 0 or null as the OFFERED value. I have a feeling this involves a join of sorts, but so far what I have tried doesn't produce the classes with nothing offered.
The table normally looks like
REFERENCE_NO DATESTART TIME TIME EID ROOMID COURSENUMBER
------------ --------- ---- ---- ---------- ---------- ----
256 03-MAR-11 0930 1100 2 2 PD01
257 03-MAY-11 0930 1100 12 7 PD01
258 18-MAY-11 1230 0100 12 7 PD01
259 24-OCT-11 1930 2015 6 2 CS01
260 17-JUN-11 1130 1300 6 4 CS01
261 25-MAY-11 1900 2000 13 6 HY01
262 25-MAY-11 1900 2000 13 6 HY01
263 04-APR-11 0930 1100 13 5 ST03
264 13-SEP-11 1930 2100 6 4 ST03
265 05-NOV-11 1930 2100 6 5 ST03
266 04-FEB-11 1430 1600 6 5 ST03
267 02-JAN-11 0630 0700 13 1 TB01
268 01-FEB-11 0630 0700 13 1 TB01
269 01-MAR-11 0630 0700 13 1 TB01
270 01-APR-11 0630 0700 13 1 TB01
271 01-MAY-11 0630 0700 13 1 TB01
272 14-MAR-11 0830 0915 4 3 AY03
273 19-APR-11 0930 1015 4 3 AY03
274 17-JUN-11 0830 0915 14 3 AY03
275 14-AUG-09 0930 1015 14 3 AY03
276 03-MAY-09 0830 0915 14 3 AY03
SELECT
coursenumber,
COUNT(CASE WHEN datestart BETWEEN (sysdate-365) AND sysdate THEN 1 END) AS Offered
FROM class
GROUP BY coursenumber;
So, as you can see, this particular problem doesn't need a join.
I think something like this should work for you, by just doing it as a subquery.
SELECT distinct c.coursenumber,
(SELECT COUNT(*)
FROM class
WHERE class.coursenumber = c.coursenumber
AND datestart BETWEEN (sysdate-365) AND sysdate
) AS Offered
FROM class c
I like jschoen's answer better for this particular case (when you want one and only one row and column out of the subquery for each row of the main query), but just to demonstrate another way to do it:
select t1.coursenumber, nvl(t2.cnt,0)
from class t1 left outer join (
select coursenumber, count(*) cnt
from class
where datestart between (sysdate-365) AND sysdate
group by coursenumber
) t2 on t1.coursenumber = t2.coursenumber