Finding out how two tables are connected by looking in a third - sql

I have a couple of tables:
boxes Table ToysTable Kitchen Table
boxId | ID Name | ID Type | ID
------------- -------------- -----------
1 | TOY1 Ball | TOY1 Fork | KIT1
1 | TOY2 Car | TOY2 Knife | KIT2
1 | KIT1 Puzzle | TOY3 Spoon | KIT3
2 | KIT2
I want to find what box contains my stuff.
So if I ask:
What box contains a fork and my car toy.
I want to get the Id of that box, in this case 1.
How would I do this ?
Sqlfiddle: http://sqlfiddle.com/#!4/11b0a/3/0
Edit:
Updated column name on kitchen to type.
Edit2:
Final solution became something like this (Thanks Gordon):
select b.boxid
from boxes b left join
(select id, 'toy' type
from toys t
where t.name in ('Car', 'Fork')
union all
select id, 'kitchen' type
from kitchen k
where k.name in ('Car', 'Fork')
) tk
on b.id = tk.id
group by b.boxid
having count(distinct tk.type) = 2;

This is a bit tricky, because the id could be to either table. One solution is group by with a union all. Here is a generic approach, assuming that the ids in the two reference table have different values:
select b.boxid
from boxes b left join
(select id, name
from toys t
union all
select id, name
from kitchen k
) tk
on b.id = tk.id
group by b.boxid
having sum(case when tk.name = 'Car' then 1 else 0 end) > 0 and
sum(case when tk.name = 'Fork' then 1 else 0 end) > 0;
Note: In MySQL, I would write this query as:
select b.boxid
from boxes b left join
(select id, name
from toys t
where t.name in ('Car', 'Fork')
union all
select id, name
from kitchen k
where k.name in ('Car', 'Fork')
) tk
on b.id = tk.id
group by b.boxid
having count(distinct name) = 2;
You could write it this way in any SQL dialect, actually.

Related

How do I output a table that an id have different gender in their game

I am not sure how to write the title of this question so the title might sound really confusing so please look at here. let suppose we have these two tables.
The first table is id, name, gender. This is the id of their game like login where name and gender is their name and gender in real life.
Where in the second table, name, and gender refer to in-game gender and name refer to in-game name as well.
id | name | gender id | name | Gender
---+------+---- ----+-------------+--------
1 | A | F 1 | a | F
2 | B | M and 1 | b | F
3 | C | M 2 | c | F
4 | D | M 3 | d | M
3 | e | M
3 | f | F
4 | g | M
4 | h | M
We want to select the id, name, and gender(in the first table), and the number of characters that an id had but that doesn't have the same gender as real-life. This might sound really confusing so here is what should be the output is
id | name | gender| #Character
---+-------+-------+-----------
2 B M 1
3 C M 3
id1: The reason it doesn't output id 1 is that id = 1 is F and she created 2 characters in the game, but both of the characters are F so she didn't 'switch' her gender so we do not print the row.
id2: We select id 2 because id = 2 is M in real-life, but he in-game character is F so we select this row.
id3: He is M in real life and one of his characters is F so we select this row
id4: He is M and none of his character is F so we don't need to print it on the screen.
Hopefully, you get what I'm trying to do here.
select id, name, gender,
(select count(*) from table_2 as t2 where t2.id = t1.id group by id) as #Character
from table_1 as t1
order by login;
Above query will print every id, name and gender and number of characters an id had, but this is not what I wanted. What should I change my code so that it works as what I Intended to do?
Actually it is simple.
We want to select the id, name, and gender(in the first table), and ... characters that an id had but that doesn't have the same gender as real-life.
select
*
from
t1
where
exists (select 1 from t2 where t1.id = t2.id and t1.gender <> t2.gender)
We want to select the id, name, and gender(in the first table), and the number of characters ...
select
*,
(select count(*) from t2 where t1.id = t2.id) as "#Character"
from
t1
where
exists (select 1 from t2 where t1.id = t2.id and t1.gender <> t2.gender)
Now that I understand the problem:
select t1.*, count(*)
from table_1 t1 join
table_2 t2
on t2.login = t1.login
group by t1.id
having count(*) filter (where t2.gender <> t1.gender) > 0;
In other words, you can filter in the having clause.
I think id and login could be confused -- your sample query does not match the sample data. But you should get the idea.
Try this:
select id,
(select count(*) from table_2 as t2 where t2.id = t1.id group by id having count(*) >1) as #Character
from table_1 as t1
order by login;

SQL - Distinct count between two tables

I'm having a mind lapse on what I believe is a relatively easy script. Hopefully I'm overthinking the logic.
What I'm trying to do is perform two counts on a distinct column which is right joined.
What I want is:
count(a.book_id) as count_of_books
count(b.book_ref_number) as count_of_losses
Expected Output
--------------------------------------------------------
| Book | count_of_books | count of losses|
--------------------------------------------------------
|Hunger Games | 76 | 31 |
--------------------------------------------------------
|Hop on Pop | 27 | 6 |
--------------------------------------------------------
|Pout Pout Fish | 138 | 43 |
--------------------------------------------------------
I have tried a couple different scripts. Here are the two scripts I've tried.
(select count(*) from Inventory_Table x ) Count1,
(select count(*) from Loss_table b ) Count2
from Inventory_Table x
right join Loss_table b on b.book_ref_number = x.book_id
where rownum < 20
select
a.book_name,
count(distinct a.book_id),
count(b.book_ref_number)
from Inventory_Table x
right join Loss_table b on trim(b.book_ref_number) = trim(a.book_id)
Results I get
--------------------------------------------------------
| Book | count_of_books | count of losses|
--------------------------------------------------------
|Moby Dick | 4376 | 2574 |
--------------------------------------------------------
I'm looking for guidance in my neglectful mistake. Thank you in advance
and rownum <20 doesn't make sense. you are limiting your result set with 20 records.
try this:
select * from (
select
a.mrch_Nr,
count(distinct a.fdr_trac_nr),
count(b.auth_id)
from DATASTORE_FD.DEB_CRD_AUTH_LOG_REC a
right join jordab26.ft b on trim(b.auth_id) = trim(a.fdr_trac_nr)
where a.auth_log_dt between '20200101' and '20200408'
group by a.mrch_nr
)
where rownum < 20
Try this, I'm not sure about rownum < 20. Also, make sure your add correct group by condition.
select sum(case book_id when null then 0 else 1 end ) count_of_books,
sum(case book_ref_number when null then 0 else 1 end ) count_of_losses
from Inventory_Table x
right join Loss_table b on b.book_ref_number = x.book_id
where rownum < 20
Is this what you want?
Select distinct bookname,
count(distinct
a.bookid)+sum(
case when a.bookid IS NULL
THEN 1 END) ,
count(distinct b.id) as lossid
From inventary_table a
Left Join
Loss_table b
On
a.bookid=b.book_ref_number
SELECT book_name,COUNT(book_id),COUNT(book_ref_id) FROM Inventory_Table right join Loss_table on book_ref_number = book_id GROUP BY book_name
But if you need all the books in Inventory and only matching books from Loss_table then it should be left join:
SELECT book_name,COUNT(book_id),COUNT(book_ref_id) FROM Inventory_Table leftjoin Loss_table on book_ref_number = book_id GROUP BY book_name
0
SELECT book_name,COUNT(book_id),COUNT(book_ref_id)
FROM Inventory_Table
right join Loss_table on book_ref_number = book_id GROUP BY book_name

Type of join based on condition postgres

I have 2 tables, where based on type of and item in table A, I would either like to force existence in another table or not require it (in order to return this id)
I wrote the following however I am getting SQL error.
How can I achieve this behaviour?
SELECT
item.id, delivery
FROM
item
(CASE
WHEN item.id not in (select ad_object_id from delivery) THEN
LEFT JOIN
ad_object_delivery
ON
item.id = ad_object_id
ELSE
JOIN
ad_object_delivery
ON
item.id = ad_object_id
END
)
Example data:
item
id | name | type
1 | John | socks
2 | Daniel | pants
3 | Barak | shirt
delivery
id | item_id | delivery
1 | 1 | UK
1 | 1 | US
definition
id | item_id | definition
1 | 1 | UK
1 | 2 | IL
I would like to get as a result only John and Barak records, because Daniel appears only in delivery but not in definition. Barak appears in neither so it's ok.
I think you want something like this:
SELECT i.id, COALESCE(d.delivery, aod2.delivery)
FROM item i LEFT JOIN
delivery d
ON i.id = d.ad_object_id LEFT JOIN
ad_object_delivery aod2
ON i.id = aod2.ad_object_id AND d.ad_object_id IS NULL
WHERE d.ad_object_id IS NOT NULL OR aod2.ad_object_id IS NOT NULL;
This matches to ad_object_delivery only when delivery does not exist. Note that you need to adjust the SELECT clause to select columns from the two tables.
If understand correctly you need JOIN with UNION ALL :
select i.id, i.name, dl.delivery
from item i inner join
delivery dl
on dl.item_id = i.id inner join
definition df
on df.item_id = i.id
union all
select i.id, i.name, null
from item i
where not exists (select 1 from delivery dl where dl.item_id = i.id) and
not exists (select 1 from definition df where df.item_id = i.id);
A confusing requirement, but I think its this:
select i.*
from item i
left join (select distinct item_id from delivery) del on del.item_id=i.id
left join (select distinct item_id from def) def on def.item_id=i.id
where (del.item_id is null and def.item_id is null)
or (del.item_id is not null and def.item_id is not null)

Compare Multiple rows In SQL Server

I have a SQL Server database full of the following (fictional) data in the following structure:
ID | PatientID | Exam | (NON DB COLUMN FOR REFERENCE)
------------------------------------
1 | 12345 | CT | OK
2 | 11234 | CT | OK(Same PID but Different Exam)
3 | 11234 | MRI | OK(Same PID but Different Exam)
4 | 11123 | CT | BAD(Same PID, Same Exam)
5 | 11123 | CT | BAD(Same PID, Same Exam)
6 | 11112 | CT | BAD(Conflicts With ID 8)
7 | 11112 | MRI | OK(SAME PID but different Exam)
8 | 11112 | CT | BAD(Conflicts With ID 6)
9 | 11123 | CT | BAD(Same PID, Same Exam)
10 | 11123 | CT | BAD(Same PID, Same Exam)
I am trying to write a query with will go through an identify everything that isn't bad as per my example above.
Overall, a patient (identified by PatientId) can have many rows, but may not have 2 or more rows with the same exam!
I have attempted various modifications of exams I found on here but still with no luck.
Thanks.
You seem to want to identify duplicates, ranking them as good or bad. Here is a method using window functions:
select t.id, t.patientid, t.exam,
(case when cnt > 1 then 'BAD' else 'OK' end)
from (select t.*, count(*) over (partition by patientid, exam) as cnt
from table t
) t;
use Count() over() :
select *,case when COUNT(*) over(partition by PatientID, Exam) > 1 then 'bad' else 'ok'
from yourtable
You can also use:
;WITH CTE_Patients
(ID, PatientID, Exam, RowNumber)
AS
(
SELECT ID, PatientID, Exam
ROW_NUMBER() OVER (PARTITION BY PatientID, Exam ORDER BY ID)
FROM YourTableName
)
SELECT TableB.ID, TableB.PatientID, TableB.Exam, [DuplicateOf] = TableA.ID
FROM CTE_Patients TableB
INNER JOIN CTE_Patients TableA
ON TableB.PatientID = TableA.PatientID
AND TableB.Exam = TableA.Exam
WHERE TableB.RowNumber > 1 -- Duplicate rows
AND TableA.RowNumber = 1 -- Unique rows
I have a sample here: SQL Server – Identifying unique and duplicate rows in a table, you can identify unique rows as well as duplicate rows
If you don't want to use a CTE or Count Over, you can also group the Source table, and select from there...(but I'd be surprised if #Gordon was too far off the mark with the original answer :) )
SELECT a.PatientID, a.Exam, CASE WHEN a.cnt > 1 THEN 'BAD' ELSE 'OK' END
FROM ( SELECT PatientID
,Exam
,COUNT(*) AS cnt
FROM tableName
GROUP BY Exam
,PatientID
) a
Select those patients that never have 2 or more exams of same type.
select * from patients t1
where not exists (select 1 from patients t2
where t1.PatientID = t2.PatientID
group by exam
having count(*) > 1)
Or, if you want all rows, like in your example:
select ID,
PatientID,
Exam,
case when exists (select 1 from patients t2
where t1.PatientID = t2.PatientID
group by exam
having count(*) > 1) then 'BAD' else 'OK' end
from patients

Using the same table twice in a FROM clause

I have a table that looks like this:
1 | 'Frank' | 'A'
2 | 'Frank' | 'B'
3 | 'Tom' | 'A'
4 | 'Tom' | 'B'
And I want an output that looks like this:
Frank | A | B
Tom | A | B
I've come up with this:
SELECT N.Name, N.Surname, M.Surname
FROM NAMES N, NAMES M
WHERE N.Surname = 'B' AND M.Surname = 'A'
GROUP BY N.Name;
This seems to work, but I don't know if it is a good practise to use the same table twice in the FROM clause, or if the performance will be affected by this on large tables.
Is there a simpler solution?
Having more than one table behind from is old-fashioned: you can make your join more readable with the standard on syntax:
from Names n
join Names m
on n.name = m.name
A self-join with a restrictive condition isn't too expensive. And Name is a pretty restrictive condition: only 2 records will share the name.
You could write it a little bit more efficient in a database that supports row_number(). That allows you to run the query without a join:
select Name
, max(case when rn = 1 then Surname end) as Surname1
, max(case when rn = 2 then Surname end) as Surname2
, max(case when rn = 3 then Surname end) as Surname3
from (
select row_number() over(
partition by Name
order by Surname) as rn
, Name
, Surname
from YourTable
) SubQueryAlias
group by
Name