Select count from different tables with a field in common - sql

So here is my query:
SELECT COUNT( tab1.id_z ) AS Count, tab_tot.name
FROM tab1
INNER JOIN tab_id ON (tab1.id_key = tab_id.id_key)
INNER JOIN tab_tot ON (tab_id.id_z = tab_tot.id_z)
WHERE tab1.id_c = 10888 GROUP BY tab_id.id_z
In table tab_id there are 6 records with the same id_z but i receive 1 in Count.
How can I do?
Edit - this is my schema:
tab1
id_key | id_c
tab_id
id_key |id_z
tab_tot
id_z | description
Though I have more records in tab_id, I "count" ever 1

Related

Return only one row based on search

Query
select
a.id,
a.ba,
b.status,
b.custid
from balist as a
inner join customer as b
on a.ba = b.ba
I have a table "balist" that has a list of (ba) and i inner join table "customer" on (ba) and right now by output is like the following
id
ba
status
custid
1
ba-1234455
A
123-321-123-321a
2
ba-1234455
I
123-321-123-321a
3
ba-1234457
A
123-321-123-321b
4
ba-1234458
A
123-321-123-321c
5
ba-1234459
I
123-321-123-321d
and I want to return all A and I status but remove the row that has status I that also have a A status. Like the following.
I have a table customer like the following
id
ba
status
custid
1
ba-1234455
A
123-321-123-321a
3
ba-1234457
A
123-321-123-321b
4
ba-1234458
A
123-321-123-321c
5
ba-1234459
I
123-321-123-321d
You could use a row_number() to filter your resulting rows eg
SELECT
id,ba,status,custid
FROM (
SELECT
a.id,
a.ba,
b.status,
b.custid,
ROW_NUMBER() OVER (
PARTITION BY a.ba
ORDER BY b.status ASC
) as rn
FROM
balist as a
INNER JOIN
customer as b ON a.ba = b.ba
)
WHERE rn=1
Let me know if this works for you.

Filtering Join in Oracle DB

Problem:
Each KEY in Table A should have one RF record and one SJ record however I have some duplicated SJ records.
Objective:
I wish to use the SJ records in Table B to identify which SJ record in Table A to keep.
Info:
Table A and Table B share a KEY and SEQ_NBR field.
Inputs:
Table A looks as follows
KEY ID_TYPE SEQ_NBR BUS_NAME
1234 RF 1 COMP_A
1234 SJ 2 COMP_B
1234 SJ 4 COMP_C
5678 RF 1 COMP_L
5678 SJ 2 COMP_M
5678 SJ 3 COMP_N
Table B looks as follows
KEY SEQ_NBR BUS_NAME
1234 2 COMP_B
5678 3 COMP_N
Desired Outcome:
My output would look as follows
KEY ID_TYPE SEQ_NBR BUS_NAME
1234 RF 1 COMP_A
1234 SJ 2 COMP_B
5678 RF 1 COMP_L
5678 SJ 3 COMP_N
Here is one way:
select key, id_type, seq_nbr, bus_name
from (
select a.*,
row_number() over (partition by a.key, a.id_type
order by b.key) as rn
from a left outer join b on a.key = b.key and a.seq_nbr = b.seq_nbr
)
where rn = 1
;
The left outer join adds columns from table b to those of table a. We need that for a single purpose: as we partition by key and id_type, we have partitions of either a single row or (two or more) rows. In the latter case, only one row has a non-null value in b.key. If we order by b.key, the row with non-null b.key will get row number = 1 (and we don't care about the rest).
Then the outer query simply keeps all the rows with row number = 1 and ignores the rest.
An alternative solution, using the union all of the two tables (slightly modified as needed) and basic aggregation using the last aggregate function:
select key, id_type,
min(seq_nbr) keep (dense_rank last order by source) as seq_nbr,
min(bus_name) keep (dense_rank last order by source) as bus_name
from (
select 'A' as source, a.* from a
union all
select 'B', key, 'SJ', seq_nbr, bus_name from b
)
group by key, id_type
;
You can test both to see which is more efficient on your data (if performance is important).
Here goes your code:
select * from tablea a
where exists
(select 1 from tableb b where b.key=a.key and b.seq_nbr=a.seq_nbr)
or not exists (select tablea.id_type from tablea inner join tableb on tablea.key=tableb.key and tablea.SEQ_NBR=tableb.SEQ_NBR and tablea.id_type=a.id_type)
If I understand correctly, you can count the number of duplicates. Then use left join and filter based on both the count and the match:
select a.*
from (select a.*,
count(*) over (partition by key, id_type) as cnt
from a
) a left join
b
on b.key = a.key and
b.seq_nbr = a.seq_nbr and
b.bus_name = a.bus_name
where cnt = 1 or b.key is not null;

SQL Query: Count "id" occurrences in two tables

I have these 3 tables and I am trying to count, how many "hints" and "quizzes" are there for specific town id.
db_town
id
town
1
New York
db_hint
id
town_id
hint
1
1
test
db_quiz
id
town_id
quiz
1
1
quiz 1
2
1
quiz 2
I am using this statement, but it does not work :(
SELECT count(q.id),count(h.id) FROM `db_town` t LEFT JOIN `db_quiz` q ON t.id = q.town_id LEFT JOIN `db_hint` h ON t.id = h.town_id WHERE t.id = 1 GROUP BY t.id
and it produces this result:
count(q.id)
count(h.id)
2
2
Do I need to use two statements? Or is it possible to query it in a single SQL statement? I am using MariaDB.
You can use union all and aggregation:
select town_id, sum(is_hint), sum(is_quiz)
from ((select town_id, 1 as is_hint, 0 as is_quiz
from hints
) union all
(select town_id, 0, 1
from quizzes
)
) t
group by town_id;
Alternatively, you can use correlated subqueries:
select t.*,
(select count(*) from hints h where h.town_id = t.id),
(select count(*) from quizzes q where q.town_id = t.id)
from towns t;
Two things to look out for:
JOINs are likely to multiply rows and throw off the counts.
Getting 0 values if a town has no hints or quizzes.
You can use COUNT (DISTINCT) if both the hint id and the quiz id are unique.
SELECT
count(distinct q.id),count(distinct h.id)
FROM `db_town` t
LEFT JOIN `db_quiz` q ON t.id = q.town_id
LEFT JOIN `db_hint` h ON t.id = h.town_id
WHERE t.id = 1 GROUP BY t.id

SQL | List all all tuples(a, b, c) if there exists another tuple with equal (b,c)

I have three tables where the bold attribute(s) is the primary key
Restaurants(restaurant_ID, name, ...)
resturant_ID, name, ...
1, Macdonalds
2, Hubert
3, Dorsia
... ...
Identifier(restaurant_ID, food_ID)
restaurant_ID, food_ID, ...
1, 1
1, 4
2, 1
2, 7
... ...
Food(food_ID, name, ...)
food_ID food_name
1 Chips
2 Burgers
3 Salmon
... ...
Using postgres I want to list out all restaurants (restaurant_id and name - 1 row per restaurant) that have share the exact same set of foods with at least one other restaurant.
For example, let's say
Restaurant with ID "1" has only associated food_id's 1 and 4 as shown in Identifier
Restaurant with ID "3" has only associated food_id's 4 and 1 as shown in Identifier
Restaurant with ID "7" has only associated food_id's 6 as shown in Identifier
Restaurant with ID "9" has only associated food_id's 6 as shown in Identifier
Then output
Restaurant_id name
1 name1
3 name3
7 ...
9 ...
Any help would be greatly appreciated!
Thank you
Use the aggregate function string_agg() to get the full list of foods for each restaurant:
with cte as (
select restaurant_ID,
string_agg(food_ID::varchar(10),',' order by food_ID) foods
from identifier
group by restaurant_ID
)
select r.*
from Restaurants r inner join cte c
on c.restaurant_ID = r.restaurant_ID
where exists (select 1 from cte where restaurant_ID <> c.restaurant_ID and foods = c.foods)
But I would prefer to group restaurants based on matching foods:
with cte as (
select restaurant_ID,
string_agg(food_ID::varchar(10),',' order by food_ID) foods
from identifier
group by restaurant_ID
)
select string_agg(r.name, ',') restaurants
from Restaurants r inner join cte c
on c.restaurant_ID = r.restaurant_ID
group by foods
having count(*) > 1
See the demo.
Here is a way to get the unique set of resturants having exactly same food items. This uses array_agg() and array_to_string() functions
With cte as
(select T.restaurant_id, array_to_string(array_agg(food_id), ',') as food_list
from
(select *
from Identifier t1
order by restaurant_id, food_id) T
group by T.restaurant_id)
select
concat(r1.name,',',r2.name) as resturant_names,
t1.restaurant_id as restaurant_id1,
r1.name as restaurant_1,
t2.restaurant_id as restaurant_id2,
r2.name as restaurant_2,
t1.food_list as common_food_ids
from cte t1
join cte t2
on t1.restaurant_id < t2.restaurant_id
and t1.food_list = t2.food_list
left join Restaurants r1
on t1.restaurant_id = r1.restaurant_id
left join Restaurants r2
on t2.restaurant_id = r2.restaurant_id;
EDIT : Here is a dB fiddle - https://dbfiddle.uk/?rdbms=postgres_12&fiddle=e2de05edfbe036cc0d81c64d60f0b599 . Also, just for reference, solution to the same problem in Oracle using listagg function - https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=12785c3d5abbca97be5d44dd45a6da4a
Update : Below query addresses the update output format of the question.
With cte as
(select T.restaurant_id, array_to_string(array_agg(food_id), ',') as food_list
from
(select *
from Identifier t1
order by restaurant_id, food_id) T
group by T.restaurant_id)
select
--concat(r1.name,',',r2.name) as resturant_names,
t1.restaurant_id as restaurant_id,
r1.name as restaurant--,
--t2.restaurant_id as restaurant_id2,
--r2.name as restaurant_2,
--t1.food_list as common_food_ids
from cte t1
join cte t2
on t1.restaurant_id = t2.restaurant_id
and t1.food_list = t2.food_list
left join Restaurants r1
on t1.restaurant_id = r1.restaurant_id
left join Restaurants r2
on t2.restaurant_id = r2.restaurant_id;
As I understand your question, you want all restaurants that have the same list of foods as restaurant 1.
If so, that's a relation division problem. Here is an approach using joins and aggregation:
select r.name
from identifier i1
inner join identifier i2 on i2.food_id = i1.food_id
inner join restaurant r on r.restaurant_id = i2.restaurant_id
where i1.restaurant_id = 1
group by r.restaurant_id
having count(*) = (select count(*) from identifier i3 where i3.restaurant_id = 1)

SQL deleting records with group by multiple tables

I am trying to delete duplicate records in a table but on if they are duplicate per a record from another.
The following query gets me the number of duplicate records per 'bodyshop'.
Im trying to delete multiple invoices for each bodyshop.
SELECT
inv.InvoiceNo, job.BodyshopId, COUNT(*)
FROM
[Test].[dbo].[Invoices] as inv
join [Test].[dbo].Repairs as rep on rep.Id = inv.RepairId
join [Test].[dbo].Jobs as job on job.Id = rep.JobsId
GROUP BY
inv.InvoiceNo, job.BodyshopId
HAVING
COUNT(*) > 1
I want the duplicate invoice numbers per bodyshop to be deleted but i do want the original one to remain.
InvoiceNo BodyshopId (No column name)
29737 16 2
29987 16 3
30059 16 2
23491 139 2
23608 139 3
23867 139 4
23952 139 3
I only want invoice number 29737 to be once against bodyshopid 16 etc.
Hope that makes sense
Thanks
Perhaps this :
with cte as (
SELECT
inv.ID, inv.InvoiceNo, job.BodyshopId, rn = row_number() over (partition by inv.InvoiceNo, job.BodyshopId order by inv.InvoiceNo, job.BodyshopId)
FROM
[Test].[dbo].[Invoices] as inv
join [Test].[dbo].Repairs as rep on rep.Id = inv.RepairId
join [Test].[dbo].Jobs as job on job.Id = rep.JobsId
)
delete t1
from [Test].[dbo].[Invoices] t1 inner join cte t2 on t1.ID = t2.ID
where t2.rn > 1
Edit 1 - Your comments are trues. So a solution is to add an identity column to the invoice table. I've adapt my query.
To add / remove an identity column :
alter table [Test].[dbo].[Invoices] id int identity(1,1)
drop column id
You may run the following as two records are same so, Group by will return single row for same invoice:
DELETE FROM inv where id not in (
SELECT Max(inv.id) FROM (
SELECT
inv.id, inv.InvoiceNo, job.BodyshopId, COUNT(*)
FROM
[Test].[dbo].[Invoices] as inv
join [Test].[dbo].Repairs as rep on rep.Id = inv.RepairId
join [Test].[dbo].Jobs as job on job.Id = rep.JobsId
GROUP BY
inv.InvoiceNo, job.BodyshopId
HAVING
COUNT(*) > 1
) TMP_TABLE )
id is the primary key.
General SQL. Modify if needed for sql-server.