SQL MAX query using 3 tables - sql

I'm having difficulty querying an SQL database.
| patient_id | episode_number | attend_practitioner | pract_assignment_date |
| ---------- | -------------- | ------------------- | --------------------- |
| 111 | 4 | 4444 | 01/05/2017 |
| 222 | 8 | 5555 | 03/17/2017 |
| 222 | 8 | 6666 | 03/20/2017 |
| 222 | 9 | 7777 | 04/10/2017 |
| 333 | 2 | 5555 | 10/08/2017 |
| 444 | 7 | 7777 | 08/09/2017 |
| patient_id | episode_number | backup_practitioner | date_of_assignment |
| ---------- | -------------- | ------------------- | ------------------ |
| 111 | 4 | | |
| 222 | 8 | 7777 | 03/17/2017 |
| 222 | 8 | 4444 | 05/18/2017 |
| 222 | 9 | | |
| 333 | 2 | 4444 | 10/08/2017 |
| 333 | 2 | 5555 | 10/19/2017 |
| patient_id | episode_number | admit_date |
| ---------- | -------------- | ---------- |
| 111 | 4 | 01/05/2017 |
| 222 | 8 | 03/17/2017 |
| 222 | 9 | 03/20/2017 |
| 333 | 2 | 10/08/2017 |
I'm looking for an SQL query where I can enter a staff_id and then have it return all the open episodes they are currently assigned to. Result:
| staff_id | patient_id | episode_number | admit_date | date_of_assignment |
| -------- | ---------- | -------------- | ---------- | ------------------ |
| 4444 | 111 | 4 | 01/05/2017 | 01/05/2017 |
| 4444 | 222 | 8 | 03/17/2017 | 05/18/2017 |
I don't understand how the SQL handles aliases in the query.
The SQL doesn't know what to do with SQL window functions such as OVER, LAG(), LEAD(), etc. So I'm using self joins along with the MAX() function. Maybe this is an older SAP SQL server.
I don't know whether capitalization in the SQL query is irrelevant.

This is a bit hard to work out from your question. I thin you need to most recent assignment for each patient/episode.
You could do this with a second sub-query using max, or you could use not exists as per below.
As you mentioned, it would be much easier with a row_number() and or CTE's, but this is bulk standard sql version.
select s.staff_id, e.patient_id, e.episode_number as episode_id, e.admit_date, s.date_of_assignment, c.last_date_of_service
from episode_history e
join (
select patient_id, episode_number, attend_practitioner as staff_id, pract_assignment_date as date_of_assignment
from history_attending_practitioner
union
select patient_id, episode_number, backup_practitioner as staff_id, date_of_assignment as date_of_assignment
from user_practitioner_assignment
where backup_practitioner is not null
) s on s.patient_id=e.patient_id and s.episode_number=e.episode_number
and not exists(select * from history_attending_practitioner a2 where a2.patient_id=s.patient_id and a2.episode_number=s.episode_number and a2.pract_assignment_date>s.date_of_assignment)
and not exists(select * from user_practitioner_assignment a3 where a3.patient_id=s.patient_id and a3.episode_number=s.episode_number and a3.date_of_assignment>s.date_of_assignment)
join view_episode_summary_current c on c.patient_id=e.patient_id and c.episode_number=e.episode_number
where e.discharge_date is null

After tweaking the query items, this is the SQL query version that returned the correct results:
SELECT
t1.staff_id
, t1.patient_id
, t3.admit_date
, t1.episode_number
, t1.date_of_assignment
FROM
/* select the most recent attending practitioner entry from table1 for the patient and episode */
(SELECT attending_practitioner AS staff_id, patient_id, episode_number, pract_assignment_date AS date_of_assignment
FROM table1 AS t1a
WHERE t1a.pract_assignment_date =
(SELECT MAX(pract_assignment_date)
FROM table1 AS t1b
WHERE t1b.patient_id = t1a.patient_id
AND t1b.episode_number = t1a.episode_number)
UNION
/* select the most recent practitioner entry from table2 for the patient and episode */
SELECT backup_practitioner AS staff_id, patient_id, episode_number, date_of_assignment
FROM table2 AS t2a
WHERE t2a.date_of_assignment =
(SELECT MAX(date_of_assignment)
FROM table2 AS t2b
WHERE t2b.patient_id = t2a.patient_id
AND t2b.episode_number = t2a.episode_number)
) AS t1
INNER JOIN
/* filter out closed episodes by using the table3 */
(SELECT patient_id AS patient_id2, episode_number AS episode_number2, admit_date
FROM table3) AS t3
ON t3.patient_id = t1.patient_id
AND t3.episode_number2 = t1.episode_number
WHERE t1.staff_id = '4444'

Related

Replace nulls of a column with column value from another table

I have data flowing from two tables, table A and table B. I'm doing an inner join on a common column from both the tables and creating two more new columns based on different conditions. Below is a sample dataset:
Table A
| Id | StartDate |
|-----|------------|
| 119 | 01-01-2018 |
| 120 | 01-02-2019 |
| 121 | 03-05-2018 |
| 123 | 05-08-2021 |
TABLE B
| Id | CodeId | Code | RedemptionDate |
|-----|--------|------|----------------|
| 119 | 1 | abc | null |
| 119 | 2 | abc | null |
| 119 | 3 | def | null |
| 119 | 4 | def | 2/3/2019 |
| 120 | 5 | ghi | 04/7/2018 |
| 120 | 6 | ghi | 4/5/2018 |
| 121 | 7 | jkl | null |
| 121 | 8 | jkl | 4/4/2019 |
| 121 | 9 | mno | 3/18/2020 |
| 123 | 10 | pqr | null |
What I'm basically doing is joining the tables on column 'Id' when StartDate>2018 and create two new columns - 'unlock' by counting CodeId when RedemptionDate is null and 'Redeem' by counting CodeId when RedmeptionDate is not null. Below is the SQL query:
WITH cte1 AS (
SELECT a.id, COUNT(b.CodeId) AS 'Unlock'
FROM TableA AS a
JOIN TableB AS b ON a.Id=b.Id
WHERE YEAR(a.StartDate) >= 2018 AND b.RedemptionDate IS NULL
GROUP BY a.id
), cte2 AS (
SELECT a.id, COUNT(b.CodeId) AS 'Redeem'
FROM TableA AS a
JOIN TableB AS b ON a.Id=b.Id
WHERE YEAR(a.StartDate) >= 2018 AND b.RedemptionDate IS NOT NULL
GROUP BY a.id
)
SELECT cte1.Id, cte1.Unlocked, cte2.Redeemed
FROM cte1
FULL OUTER JOIN cte2 ON cte1.Id = cte2.Id
If I break down the output of this query, result from cte1 will look like below:
| Id | Unlock |
|-----|--------|
| 119 | 3 |
| 121 | 1 |
| 123 | 1 |
And from cte2 will look like below:
| Id | Redeem |
|-----|--------|
| 119 | 1 |
| 120 | 2 |
| 121 | 2 |
The last select query will produce the following result:
| Id | Unlock | Redeem |
|------|--------|--------|
| 119 | 3 | 1 |
| null | null | 2 |
| 121 | 1 | 2 |
| 123 | 1 | null |
How can I replace the null value from Id with values from 'b.Id'? If I try coalesce or a case statement, they create new columns. I don't want to create additional columns, rather replace the null values from the column values coming from another table.
My final output should like:
| Id | Unlock | Redeem |
|-----|--------|--------|
| 119 | 3 | 1 |
| 120 | null | 2 |
| 121 | 1 | 2 |
| 123 | 1 | null |
If I'm following correctly, you can use apply with aggregation:
select a.*, b.*
from a cross apply
(select count(RedemptionDate) as num_redeemed,
count(*) - count(RedemptionDate) as num_unlock
from b
where b.id = a.id
) b;
However, the answer to your question is to use coalesce(cte1.id, cte2.id) as id.

SQL query to get counts from two tables

I need help to write a query to combine two tables in order to get desired result set with count of rows.
Please see below in detail:
I have two tables
Table A
| CompanyID | ProductID | ProductPartsID |
| --------- | --------- | -------------- |
| 123 | ert | q1234 |
| 234 | dfr | u678 |
| 234 | dfr | Jdsdj |
| 234 | dfr | Eewe |
| 234 | dfr | dssd |
| 234 | HJU | iui89 |
| 234 | HJU | dfsfs |
| 675 | gfd | 654 |
| 675 | tyh | 765 |
Table B
|CompanyID | ProductID |
|--------- |-----------|
| 123 | ert |
| 234 | dfr |
| 234 | HJU |
| 675 | gfd |
| 709 | tgh |
| 780 | 789 |
Resultset
Both tables has millions of records in reality.
|CompanyID | #ofTableAProductPartsID | #ofTableBProductPartsID |
|--------- |-------------------------|-------------------------|
| 123 | 1 | 1 |
| 234 | 6 | 2 |
| 675 | 2 | 1 |
e.t.c
Table B does not have ProductPartsID, but it can be taken from TableA for ProductID from TableB.
using left join and group by :
SELECT
t.CompanyID
, COUNT(t.ProductPartsID)
, COUNT(DISTINCT t2.ProductID)
FROM
TABLE1 AS t
LEFT JOIN Table2 t2
ON t.CompanyID = t2.CompanyID
AND t.ProductID = t2.ProductID
GROUP BY
t.CompanyID
with TableAPartsCount as
(
select
A.CompanyID,
count(A.ProductID) as #ofTableAProductPartsID
from TableA A
group by A.CompanyID
)
,TableBPartsCount as
(
select
CompanyID,
count(ProductID) as #ofTableBProductPartsID
from TableB
group by CompanyID
)
select
A.CompanyID,
A.#ofTableAProductPartsID,
B.#ofTableBProductPartsID
from TableBPartsCount B
join TableAPartsCount A on
A.CompanyID = B.CompanyID

How to joins in oracle based on condition

I have two tables as below:
Table Apple:
+----------+----------+---------+
| APPLE_ID | PHONE_ID | IPAD_ID |
+----------+----------+---------+
| 1 | 1001 | 2001 |
| 2 | 1002 | 2002 |
| 3 | 1003 | 2003 |
| 4 | 1004 | 2004 |
+----------+----------+---------+
Table KEEY:
+---------+----------+--------+-----------+
| KEEY_ID | NAME | DTL_ID | DEVICE_ID |
+---------+----------+--------+-----------+
| 1 | PHONE_ID | 1001 | 111 |
| 2 | PHONE_ID | 2001 | 111 |
| 3 | IPAD_ID | 2001 | 222 |
| 4 | PHONE_ID | 1003 | 444 |
| 5 | MAC_ID | 367 | 333 |
+---------+----------+--------+-----------+
Desired Output:
+----------+----------+---------+-----------------+----------------+
| APPLE_ID | PHONE_ID | IPAD_ID | PHONE_DEVICE_ID | IPAD_DEVICE_ID |
+----------+----------+---------+-----------------+----------------+
| 1 | 1001 | 2001 | 111 | 222 |
| 3 | 1003 | 2003 | 444 | null |
+----------+----------+---------+-----------------+----------------+
Code Tried So far:
SELECT
APPLE.APPLE_ID,
APPLE.PHONE_ID,
APPLE.IPAD_ID,
NULL AS IPHONE_DEVICE_ID,
KY.DEVICE_ID AS IPAD_DEVICE_ID
FROM APPLE
LEFT JOIN KEEY KY ON APPLE.IPAD_ID=KY.DTL_ID WHERE KY.NAME='IPAD_ID'
UNION
SELECT
APPLE.APPLE_ID,
APPLE.PHONE_ID,
APPLE.IPAD_ID,
KY.DEVICE_ID AS PHONE_DEVICE_ID,
NULL AS IPAD_DEVICE_ID
FROM APPLE
LEFT JOIN KEEY KY ON APPLE.PHONE_ID=KY.DTL_ID WHERE KY.NAME='PHONE_ID'
This is giving me :
+----------+----------+---------+------------------+----------------+
| APPLE_ID | PHONE_ID | IPAD_ID | IPHONE_DEVICE_ID | IPAD_DEVICE_ID |
+----------+----------+---------+------------------+----------------+
| 1 | 1001 | 2001 | 111 | (null) |
| 1 | 1001 | 2001 | (null) | 222 |
| 3 | 1003 | 2003 | 444 | (null) |
+----------+----------+---------+------------------+----------------+
I guess i need to use pivot instead of Union to get both the ids on same row.
Have you ever encountered such scenarios? Any pointers to proceed will be very helpful.
Thanks in Advance!
DDL used for the above problem:
CREATE TABLE APPLE
( APPLE_ID INTEGER,
PHONE_ID INTEGER,
IPAD_ID INTEGER);
INSERT INTO APPLE VALUES (1,1001,2001);
INSERT INTO APPLE VALUES (2,1002,2002);
INSERT INTO APPLE VALUES (3,1003,2003);
INSERT INTO APPLE VALUES (4,1004,2004);
CREATE TABLE KEEY
( KEEY_ID INTEGER,
NAME VARCHAR2(50),
DTL_ID INTEGER,
DEVICE_ID INTEGER);
INSERT INTO KEEY VALUES (1,'PHONE_ID',1001,111);
INSERT INTO KEEY VALUES (2,'PHONE_ID',2001,111);
INSERT INTO KEEY VALUES (3,'IPAD_ID',2001,222);
INSERT INTO KEEY VALUES (4,'PHONE_ID',1003,444);
INSERT INTO KEEY VALUES (5,'MAC_ID',367,333);
I think you just want two joins:
select a.*, k1.device_id as phone_id, d2.device_id as ipad_id
from apple a join
keey k1
on a.phone_id = k1.dtl_id and k1.name = 'PHONE_ID' left join
keey k2
on a.ipad_id = k2.dtl_id and k2.name = 'IPAD_ID';
Here is a db<>fiddle (it uses Postgres just because that is easier to set up in the fiddle, but the results should be the same).
Another simple way to achieve the same would be UNION:
Select apple_id, phone_id, ipad_id, SUM(PHONE_DEVICE_ID), SUM(IPAD_DEVICE_ID)
from
(Select a.apple_id, a.phone_id, a.ipad_id ,
CASE WHEN k.NAME = 'PHONE_ID' THEN k.DEVICE_ID END PHONE_DEVICE_ID, 0 as IPAD_DEVICE_ID
from apple a
JOIN keey k ON a.phone_id = k.dtl_id AND k.name = 'PHONE_ID'
UNION ALL
Select b.apple_id, b.phone_id, b.ipad_id ,
0 as PHONE_DEVICE_ID, CASE WHEN j.NAME = 'IPAD_ID' THEN j.DEVICE_ID END IPAD_DEVICE_ID
from apple b
JOIN keey j ON b.IPAD_id = j.dtl_id AND j.name = 'IPAD_ID'
) group by apple_id, phone_id, ipad_id;
O/P:
+----------+----------+---------+-----------------+----------------+
| APPLE_ID | PHONE_ID | IPAD_ID | PHONE_DEVICE_ID | IPAD_DEVICE_ID |
+----------+----------+---------+-----------------+----------------+
| 1 | 1001 | 2001 | 111 | 222 |
+----------+----------+---------+-----------------+----------------+
| 3 | 1003 | 2003 | 444 | 0 |
+----------+----------+---------+-----------------+----------------+

Having Groups based on distinct count of another column

I have a table as follow :
+-------------+-----------+------+
| GroupNumber | TeamName | Goal |
+-------------+-----------+------+
| 1 | Sales | ABC |
| 1 | Sales | ABC |
| 1 | Sales | ABC |
| 1 | Design | XYZ |
| 2 | Design | XYZ |
| 2 | Sales | XYZ |
| 2 | technical | XYZ |
| 2 | Support | XYZ |
| 3 | Sales | XYZ |
| 3 | Sales | XYZ |
| 3 | Sales | XYZ |
+-------------+-----------+------+
I want to output only the groups that have unique teams greater than 3.
Only group 2 has this condition so the output is :
Expected Output:
+-------------+-----------+------+
| GroupNumber | TeamName | Goal |
+-------------+-----------+------+
| 2 | Design | XYZ |
| 2 | Sales | XYZ |
| 2 | technical | XYZ |
| 2 | Support | XYZ |
+-------------+-----------+------+
not sure how to utilize this in subquery
SELECT count(Distinct(TeamName))
FROM mytable
group by [GroupNumber]
HAVING COUNT(Distinct[TeamName])>3
Simply put it in a Subquery:
select *
from mytable
where [GroupNumber] in
(
SELECT [GroupNumber]
FROM mytable
group by [GroupNumber]
HAVING COUNT(Distinct[TeamName])>3
)
Please try
SELECT *
FROM mytable where GroupNumber in (select GroupNumber
FROM mytable group by TeamName
HAVING COUNT(TeamName)>3)

How to compute the number of sequence on an sentence in SQLite?

I have one table which records the deliverings of some products to clients. Its basic scheme is very easy:
| DeliveryDate | IdClient | IdProduct | Quantity | Cost |
Well, one assumption is that the clients can only purchase one item at a time, and they begin purchasing Item1, eventually they could change and start buying Item2, stopping from buying Item1, and so on. That do not mean Item1, Item2, ..., ItemN is always the same product, they could vary.
So, what I want is being able to get the different items purchased for each costumer, with its initial date of purchase, and the number of sequence of each product for each client, since I could not guess in advance which item will be bought. With this scheme:
| IdClient | IdProduct | FirstTimeDate | NumSequence |
Let me explain with a sample:
Table:
| DeliveryDate | IdClient | IdProduct | Quantity | Cost |
| 2016-05-01 | 1234 | 9876 | 2 | 1000 |
| 2016-06-01 | 1234 | 9876 | 1 | 500 |
| 2016-07-01 | 1234 | 8765 | 2 | 2000 |
| 2016-08-01 | 1234 | 5432 | 3 | 3500 |
| 2016-06-01 | 3456 | 5432 | 2 | 1500 |
| 2016-07-01 | 3456 | 5432 | 1 | 700 |
| 2016-08-01 | 3456 | 9523 | 2 | 2500 |
Desired Output:
| IdClient | IdProduct | FirstTimeDate | NumSequence |
| 1234 | 9876 | 2016-05-01 | 1 |
| 1234 | 8765 | 2016-07-01 | 2 |
| 1234 | 5432 | 2016-08-01 | 3 |
| 3456 | 5432 | 2016-06-01 | 1 |
| 3456 | 9523 | 2016-08-01 | 2 |
I could manage to get everything, except the number of sequence, instead of that, with the sql sentence written below I can get the number of different products purchased for each client:
| IdClient | IdProduct | FirstTimeDate | NumOfDistinctProducts |
| 1234 | 9876 | 2016-05-01 | 3 |
| 1234 | 8765 | 2016-07-01 | 3 |
| 1234 | 5432 | 2016-08-01 | 3 |
| 3456 | 5432 | 2016-06-01 | 2 |
| 3456 | 9523 | 2016-08-01 | 2 |
And the sql (for simplicity, I run first one sql creating a temporary table, and after that I run the actual query):
// Query to create the temporary table:
CREATE TEMPORARY TABLE tmpNewDelivering AS WITH FT_CTE AS (
SELECT min(Date) ChangeDate,* FROM Deliverings WHERE Deliverings.IdProduct IN ( // Actual IdProducts // )
GROUP BY IdProduct, IdPatient
ORDER BY Deliverings.Date ASC
)
SELECT * from FT_CTE;
// Query I want to improve:
SELECT case when C.StartDate = tND.ChangeDate then "Start" else "Change" end Type, C.NumProducts NumProducts, tND.*
FROM tmpNewDelivering tND INNER JOIN (
SELECT IdClient, count(IdProduct) NumProducts, min(ChangeDate) StartDate
FROM tmpNewDelivering
GROUP BY IdClient
) C ON tND.IdClient = C.IdClient
ORDER BY tCN.Fecha DESC
SQLite does not have some capabilities that would be useful for this.
You an get the first three columns just using aggregation:
select IdClient, IdProduct, min(DeliveryDate) as FirstTimeDate
from IdClient
group by IdPatient, IdProduct;
To get the sequence, you can modify this to get the number of different products purchased on or before the current one:
| NumOfDistinctProducts
select IdClient, IdProduct, min(DeliveryDate) as FirstTimeDate,
(select count(distinct d2.IdProduct)
from Deliverings d2
where d2.IdClient = d.IdClient and
d2.DeliveryDate <= d.DeliveryDate
) as NumSequence
from Deliverings d
group by IdPatient, IdProduct;
Note: You can use a temporary table for this. If so, you can use the temporary table for the subquery. This is a good idea if you put an index on IdClient, DeliveryDate, IdProduct.