SQL: Joining tables with WHERE clauses - sql

I am trying to join two tables that share the same individual ID (key). The first table (a) is a 'wide' table with many variables including key and age, and the second table (b) is a 'long' table, including only the variables key, diagnosis_number, and diagnosis, where each individual can have multiple values of diagnosis.
I want to select columns key, age, and the primary diagnosis for individuals where:
diagnosis = "a", "b", or "c" when diagnosis_number = 1 [the 'primary diagnosis']
AND diagnosis = "y" for any of diagnosis_number = 2:20
I've tried:
SELECT main.key, main.age, diag.diagnosis
FROM a as main
INNER JOIN
(
SELECT prim.key, prim.diagnosis
FROM
(SELECT DISTICT key, diagnosis
FROM b
WHERE diagnosis IN ('a', 'b', 'c')
AND diagnosis_number = 1) as prim
INNER JOIN
(SELECT DISTICT key, diagnosis
FROM b
WHERE diagnosis = 'y'
AND diagnosis_number BETWEEN 2 AND 20) as sec
ON prim.key = sec.key) as diag
ON main.key = diag.key

Think this could be solved without subqueries/SELECTs inside of joins:
SQL Server
SELECT a.key, a.age, b.diagnosis
FROM a
INNER JOIN b
ON b.key = a.key
WHERE b.diagnosis IN ('a', 'b', 'c')
AND b.diagnosis_code = 1
AND a.key IN (SELECT b1.key
FROM b AS b1
WHERE b1.diagnosis = 'y'
AND b1.diagnosis_code BETWEEN 2 AND 20)

Related

SQL joining columns of the same table

I need help on the following SQL query. Let's say we have table_1 with these columns:
number
Customer
list
321
4514
321
2
2
5321
2
5555
If there's a number in the list column, that indicates that is that there is a list of numbers that should refer to that list. Below is a snapshot of how the final table should look. When there's a null value in the customer field it indicates that there is a list, that list number you can find the customers on that list when the number = the list. I need to change the number to make reference to the number the list belongs to.
number
Customer
list
321
4514
321
5321
2
321
5555
2
I've tried with different joins but unsuccessful:
SELECT *
FROM table_1
OUTER JOIN
(SELECT *
FROM TABLE_1
WHERE list IS NOT NULL) AS table_2 ON TABLE_1.list = table_2.list
You say that this is guaranteed to be one level only. So you can have 321->2, but not, say, 321->2->1.
Then, well, let's join and show the joined result:
select
coalesce(ref.number, t1.number) as num,
t1.customer,
ref.list
from table_1 t1
left outer join table_1 ref on ref.list = t1.number
where t1.list is null;
I guess you need to change the data (DML). Here us an example:
DROP TABLE IF EXISTS customer_list
CREATE TABLE customer_list (
number INT,
Customer INT,
list INT
);
INSERT INTO customer_list (number, Customer, list)
VALUES
(321, 4514, NULL),
(321, NULL, 2),
(2, 5321, NULL),
(2, 5555, NULL);
UPDATE A
SET [number] = B.number
,[list] = b.list
FROM customer_list A
INNER JOIN customer_list B
ON A.number = B.list
WHERE B.Customer IS NULL
DELETE FROM customer_list
WHERE Customer IS NULL;
SELECT *
FROM customer_list
If you need only to get the records:
SELECT B.number
,A.customer
,B.List
FROM customer_list A
INNER JOIN customer_list B
ON A.number = B.list
WHERE B.Customer IS NULL

SQL Join on case for a table made using subquery

I have an output from a subquery
SELECT roll, marks FROM STUDENTS WHERE std in (1,2,3,4,5,6) and belongsTo in ('A', 'B', 'C') and subject = 'maths'
which I want to join to my table only in case when the value of a column from another table is Teacher.code = 1 else I don't want this above sub table to be considered
OTHER INNER JOIN AND SELECT TABLE STATEMENT
INNER JOIN (SELECT roll, marks FROM STUDENTS WHERE std in (1,2,3,4,5,6) and belongsTo in ('A', 'B', 'C') and subject = 'maths') GRADE
ON CASE
WHEN Teacher.code = 1 THEN /* the above join should be used*/
WHEN Teacher.code= 2 THEN /* above join should not be used */
You seem to want something like this:
ON Teacher.code = 1 AND <join conditions> OR
Teacher.code = 2
Presumably, if Teacher.code is neither 1 nor 2 the ELSE 0 means you want nothing to match.

SELECT JOIN Table Operations

In my query below, I joined tables b, c, d to a by the base_id column.
However, I need to do some operations in my SELECT statement.
Since b, c, and d are not joined to each other,
is my (a.qty - (b.qty - (c.qty + d.qty))) formula computing only those tables that have the same base_id column?
SELECT (a.qty - (b.qty - (c.qty + d.qty))) AS qc_in
FROM receiving a
LEFT JOIN (
SELECT SUM(qty) AS qty, base_id
FROM quality_control bb
WHERE location_to = 6
AND is_canceled = 0
GROUP BY base_id
) b
ON b.base_id = a.base_id
LEFT JOIN (
SELECT SUM(qty) AS qty, base_id
FROM quality_control ba
WHERE location_from = 6
AND is_canceled = 0
GROUP BY base_id
) c
ON c.base_id = a.base_id
LEFT JOIN (
SELECT SUM(qty) AS qty, base_id
FROM issuance
WHERE location_from = 6
AND is_canceled = 0
GROUP BY base_id
) d
ON d.base_id = a.base_id
WHERE a.is_canceled = 0
I think you're confused by how joining works (if I'm reading the question correctly). If you have:
select *
from table1 a
join table2 b
on a.Id = b.Id
join table3c
on a.Id = c.Id
Then yes a is joined to b, and b is joined to c, but that also means that a is joined to c. One way to think of it is as one giant in-memory table that has all of the a columns then all of the b columns and then all of the c columns in the one result.
If a.Id is 1, and you select the row from b where Id is the same (1) and then you join to c where the Id is the same as a (1), then a, b and c all have the same id.
So yes, (a.qty - (b.qty - (c.qty + d.qty))) will only be doing that calculation for rows where a, b, c and d all have the same base_id .
Surely. In the nested statements you join all the tables on base_id. That means, as the result of those joins you will get a huge table containing columns from all of the joined tables with a common column you join on (base_id in your case).

SQL Query where the result has duplicate rows

So I have to write a query in SQL where I list the celebrities that have been in relationships with the same celeb. I basically list out celeb1, celeb2, and celeb3 where celeb3 has been in a relationship with both celeb1 and celeb2. Here's the query I'm using:
SELECT S1.Celeb1, S2.Celeb2, S3.name AS Celeb3
FROM Relationships S1, Relationships S2, Celebs S3
WHERE S3.name = S1.Celeb2
AND S3.name = S2.Celeb1
AND S1.Celeb1 <> S2.Celeb2;
It's difficult to know if this query is correct since it gives me 200 rows in the result but I looked at a few of the row and it looks like it's giving me the correct result where celeb3 has been in a relationship with both celeb1 and 2. The problem is that there are duplicate rows in the result. This could be due to the fact that in the relationships table, it lists out the celeb1, celeb2 relationship but it also lists the inverse celeb2, celeb1 as well. So how can I prevent the result from listing out duplicates?
Here are the two tables I'm using to do this (Relationships and Celebs).
CREATE TABLE Celebs(
name VARCHAR(30)
);
CREATE TABLE Relationships (
Celeb1 VARCHAR(30),
Celeb2 VARCHAR(30)
);
Let's look at a sample:
celeb1 celeb2
A B
B C
C D
Expected result:
A and C were both with B.
B and D were both with C.
In order to find these matches I suggest to duplicate tuples such that each couple is twice in the table (if that is not already the case).
celeb1 celeb2
A B
B A
B C
C B
C D
D C
We can already see that B and C each had two partners. Join this data set with itself so as to connect the records.
with rel as
(
select celeb1 as cel1, celeb2 as cel2 from relationships
union
select celeb2 as cel1, celeb1 as cel2 from relationships
)
select rel1.cel2 as celeb1, rel2.cel2 as celeb2, rel1.cel1 as partner
from rel rel1
join rel rel2 on rel2.cel1 = rel1.cel1 and rel2.cel2 > rel1.cel2
order by 1, 2, 3;
If Celeb3 was in a relationship with A and B, you will also get B, A as a result. To avoid that, just make a constraint that A > B:
SELECT DISTINCT S1.Celeb1, S2.Celeb2, S3.name AS Celeb3
FROM Relationships S1, Relationships S2, Celebs S3
WHERE S3.name = S1.Celeb2
AND S3.name = S2.Celeb1
AND S1.Celeb1 > S2.Celeb2
Oracle Setup:
CREATE TABLE celebs ( name ) AS
SELECT 'A' FROM DUAL UNION ALL
SELECT 'B' FROM DUAL UNION ALL
SELECT 'C' FROM DUAL UNION ALL
SELECT 'D' FROM DUAL;
CREATE TABLE relationships ( celeb1, celeb2 ) AS
SELECT 'A', 'B' FROM DUAL UNION ALL
SELECT 'B', 'C' FROM DUAL UNION ALL
SELECT 'C', 'D' FROM DUAL;
Query:
SELECT DISTINCT
c.name,
CASE c.name WHEN r.celeb1 THEN r.celeb2 ELSE r.celeb1 END AS has_relationship_with
FROM celebs c
LEFT OUTER JOIN
relationships r
ON ( c.name = r.celeb1 OR c.name = r.celeb2 );
Output:
NAME HAS_RELATIONSHIP_WITH
---- ---------------------
A B
B A
B C
C B
C D
D C
If you want A,B and do not want the inverse B,A then change the ON clause for the join to:
ON ( ( c.name = r.celeb1 AND c.name < r.celeb2 )
OR ( c.name = r.celeb2 AND c.name < r.celeb1 ) )
Query 2:
You could then group this using LISTAGG to just get one row per person:
SELECT name,
LISTAGG( rel, ',' ) WITHIN GROUP ( ORDER BY rel ) AS has_relationship_with
FROM (
SELECT DISTINCT
c.name,
CASE c.name WHEN r.celeb1 THEN r.celeb2 ELSE r.celeb1 END AS rel
FROM celebs c
LEFT OUTER JOIN
relationships r
ON ( c.name = r.celeb1 OR c.name = r.celeb2 )
)
GROUP BY name;
Output:
NAME HAS_RELATIONSHIP_WITH
---- ---------------------
A B
B A,C
C B,D
D C

Combining tables SQL Server 2005

Table 1: LocID, Prod_ID, Metric_ID, Metric_Data
Table 2: LocID, Metric_ID, Metric_Data
I need a Result Table as:
LocID
Prod_ID
Metric_ID
Metric_Data with the following conditions being met:
When Metric_ID's match, the Metric_Data will be added
When Metric_ID do not match, the relevant Metric_Data will be shown (meaning the one which has value)
Please note only a some common and some different Metric_ID's exist in Table 1 and Table 2.
How do you generate this 3rd table? I have tried all kinds of joins - full, left, right, etc.
EDIT
select
A.LocID,
A.Prod_ID,
B.Metric_ID,
coalesce(C.Metric_Data + D.Metric_Data, C.Metric_Data, D.Metric_Data) Metric_Data
from (
select LocID, Prod_ID from table1 group by LocID, Prod_ID) A
inner join (
select LocID, Metric_ID from table1 group by LocID
union
select LocID, Metric_ID from table2 group by LocID) B on A.LocID = B.LocID
left join table1 C on C.LocID = A.LocID and C.Prod_ID = A.Prod_ID and C.Metric_ID = B.Metric_ID
left join table2 D on D.LocID = A.LocID and D.Metric_ID = B.Metric_ID
Notes:
A: produces all the location and ProdID combinations
B: produces, for each location, all the possible MetricIDs from both tables
C and D: left joins to the data tables to get the Metric Data
Coalesce: returns either C + D, or if one of them is null, return the other
select
coalesce(a.LocID, b.LocID) LocID,
a.Prod_ID,
coalesce(a.Metric_ID, b.Metric_ID) Metric_ID,
coalesce(a.Metric_Data + b.Metric_Data, a.Metric_Data, b.Metric_Data) Metric_Data
from table1 a
full outer join table2 b
on a.LocID = b.LocID and a.Metric_ID = b.Metric_ID
This assumes
You are matching by the tuple (LocID, Metric_ID)
It is possible for either A or B not to have (LocID,Metric_ID) that exists in the other
The result of Metric_Data is either A+B (if both exist), or A or B if only one exists for a (LocID, Metric_ID) combination