How to join two queries with count? - sql

I have 3 tables in database:
Table 1: violation
| violation_id | violation_name |
|:-------------:|:--------------:|
|1 | No Parking |
|2 | Speed Contest |
|3 | No Helmet |
Table 2: violators
| violator_id | violation_id |
|:-------------:|:--------------:|
|1 |1 |
|2 |1 |
|3 |3 |
Table 2: previous_violator
| prev_violator_id| violation_id |
|:---------------:|:--------------:|
|1 |1 |
|2 |2 |
|3 |2 |
This view that I want:
| violation_name | Total |
|:-------------:|:--------------:|
|No Parking | 3 |
|Speed Contest | 2 |
|No Helmet | 1 |
I perform this code that joins the violator table and violation:
SELECT *,count(violators.violation_id) as vid
FROM violators
LEFT JOIN violation ON violation.violation_id = violators.violation_id
LEFT JOIN previous_violator ON previous_violator.violator_id = violators.violator_id
WHERE date_apphrehend BETWEEN '$from' AND '$to'
GROUP BY violators.violation_id
My problem is, I want to join the previous violator table that count to the total based on the violation_name.

You can first union all to get them into a single result and then count(*) it. Finally join with Violation to get names. ie:
select violation_name, count(*) as cnt
from (select violation_id from Violators
union all
select violation_id from previous_Violators) tmp
inner join Violation on tmp.violation_id = Violation.violation_id
group by Violation.violation_id, violation_name;
Sample DBFiddle demo.
PS: Sample is in postgreSQL but it would be the same for most backends. You didn't tag your backend.

Related

Fill Future date for null groupby sql(presto)

This might be easier than I'm thinking, but essentially want to fill in values that would be null for ID 2. Example below. Thanks.
Given Table:
|ID| food category | time |
:--:----------:-------
|1 |italian | 2021-10-01|
|1 | indian | 2021-10-23|
|1 | american| 2021-10-05|
|1 | mexican | 2021-10-07|
|1 | Chinese | 2021-10-09|
|1 | vietnamese| 2021-10-11|
|1 | thai | 2021-10-12|
|1 | Moroccan| 2021-9-01|
|1 | russian | 2021-7-01|
|1 | korean | 2021-4-30|
|1 | canadian| 2021-7-01|
|2 |italian | 2020-10-11|
|2 | indian | 2021-04-23|
|2 | american| 2021-10-25|
|2 | mexican | 2021-10-27|
I'd like to transform the table above by grouping by id and food category, but still have the time for ID 2 to be replaced with future dates(date_add('year',1, now()) for null time. Since there would be no record for ID 2 for the food categories of Chinese, Vietnamese, Thai, Moroccan, Russian, Korean, and Canadian these would be null, but I'd like them to still show in the group by the table and be placed by the date 1 year from now. Example of desired results below. Thank you for the help.
Desired Table:
|ID| food category | time |
:--:----------:-------
|1 |italian | 2021-10-01|
|1 | indian | 2021-10-23|
|1 | american| 2021-10-05|
|1 | mexican | 2021-10-07|
|1 | Chinese | 2021-10-09|
|1 | vietnamese| 2021-10-11|
|1 | thai | 2021-10-12|
|1 | Moroccan| 2021-9-01|
|1 | russian | 2021-7-01|
|1 | korean | 2021-4-30|
|1 | canadian| 2021-7-01|
|2 |italian | 2020-10-11|
|2 | indian | 2021-04-23|
|2 | american| 2021-10-25|
|2 | mexican | 2021-10-27|
|2 | Chinese | 2022-11-23|
|2 | vietnamese| 2022-11-23|
|2 | thai | 2022-11-23|
|2 | Moroccan| 2022-11-23|
|2 | russian | 2022-11-23|
|2 | korean | 2022-11-23|
|2 | canadian| 2022-11-23|
you can use following query
SELECT COALESCE(t1.ID,t2.ID) as ID,
COALESCE(t1.foodcategory,t2.foodcategory) as foodcategory,
CAST(COALESCE(t2.time,dateadd(year, 1, getdate())) AS DATE) time
FROM
(SELECT *
FROM
(SELECT foodcategory
FROM testTB
GROUP BY foodcategory) t1
JOIN
(SELECT id
FROM testTB
GROUP BY id) t2 on 1=1) t1
LEFT JOIN testTB t2 on t1.ID = t2.ID and t1.foodcategory = t2.foodcategory
or
WITH cte AS (
select distinct foodcategory from testTB
)
SELECT t2.ID,t1.foodcategory,CAST(COALESCE(t3.time,dateadd(year, 1, getdate())) AS DATE) time
FROM cte t1
FULL OUTER JOIN (
select distinct [ID] from testTB
) t2 on 1=1
left join testTB t3 on t2.ID = t3.ID and t1.foodcategory = t3.foodcategory
order by t2.id
demo in db<>fiddle
Use a CTE to gather the list of food categories first. Then gather the list of IDs.
WITH cteCat AS (
select distinct [food category] from table
)
, cteID AS (
select distinct [ID] from table
)
SELECT id.[ID], cat.[food category],
COALESCE(t.[time], dateadd(year, 1, getdate())) as [time]
FROM cteCat cat
, cteID id
LEFT OUTER JOIN table t
ON t.[ID] = id.[ID]
AND t.[food category] = cat.[food category]

SQL select all records if at least one record fulfils a condition

I have several tables
table1
-------------------------
|id| rec | other_rec|
-------------------------
|1 | record1 | record6 |
|2 | record2 | record8 |
|3 | record4 | record0 |
|4 | record5 | record2 |
|n | ... | ... |
------------------------
and a second table
table2
-------------------------------------------------
|id| table_nr_1_foreign_key | rec_1 | rec_2 |
-------------------------------------------------
|1 | table_nr_1_key_1 |record1 | rt1 |
|2 | table_nr_1_key_2 |record2 | rt2 |
|3 | table_nr_1_key_2 |record4 | rt3 |
|4 | table_nr_1_key_3 |record5 | rt4 |
|5 | table_nr_1_key_2 |record6 | rt5 |
|n | table_nr_1_key_n | ... | ... |
-------------------------------------------------
and an SQL query
SELECT t.id,
t.rec,
t.other_rec,
t2.rec_1
FROM table1 t1
JOIN ...[other table]
and ...[conditions start]
and ...
and ...[conditions end]
LEFT JOIN table2 t2 ON t.id = table_nr_1_foreign_key
AND t2.rec_2 = any(array['rt2'])
where ...[other condition]
now I want to select all records from table2 which have a corresponding foreign key in a table1
if and only if at least one record exists in a table2 that points to a table1.
so I want
data1,..., rt2
data1,..., rt3
data1,..., rt5
...
to be selected
but all I get is
data1,..., rt2
Update no 1.
with my inexperience I failed to accomplish selection with one sql and wrote two sql queries instead
for data selection for a main record (table 1)
the other for selecting all records from a table 2 (using in([list
of ids]))
This question can be closed / deleted.

In PostgreSQL, conditionally count rows

Background
I'm a novice Postgres user running a local server on a Windows 10 machine. I've got a dataset g that looks like this:
+--+---------+----------------+
|id|treatment|outcome_category|
+--+---------+----------------+
|a |1 |cardiovascular |
|a |0 |cardiovascular |
|b |0 |metabolic |
|b |0 |sensory |
|c |1 |NULL |
|c |0 |cardiovascular |
|c |1 |sensory |
|d |1 |NULL |
|d |0 |cns |
+--+---------+----------------+
The Problem
I'd like to get a count of outcome_category by outcome_category for those id who are "ever treated" -- defined as "id's who have any row where treatment=1".
Here's the desired result:
+----------------+---------+
|outcome_category| count |
+----------------+---------+
|cardiovascular | 3 |
|sensory | 1 |
|cns | 1 |
+----------------+---------+
It would be fine if the result had to contain metabolic, like so:
+----------------+---------+
|outcome_category|treatment|
+----------------+---------+
|cardiovascular | 3 |
|metabolic | 0 |
|sensory | 1 |
|cns | 1 |
+----------------+---------+
Obviously I don't need the rows to be in any particular order, though descending would be nice.
What I've tried
Here's a query I've written:
select treatment, outcome_category, sum(outcome_ct)
from (select max(treatment) as treatment,
outcome_category,
count(outcome_category) as outcome_ct
from g
group by outcome_category) as sub
group by outcome_category, sub.treatment;
But it's a mishmash result:
+---------+----------------+---+
|treatment|outcome_category|sum|
+---------+----------------+---+
|1 |cardiovascular |3 |
|1 |sensory |2 |
|0 |metabolic |1 |
|1 |NULL |0 |
|0 |cns |1 |
+---------+----------------+---+
I'm trying to identify the "ever exposed" id's using that first line in the subquery: select max(treatment) as treatment. But I'm not quite getting at the rest of it.
EDIT
I realized that the toy dataset g I originally gave you above doesn't correspond to the idiosyncrasies of my real dataset. I've updated g to reflect that many id's who are "ever treated" won't have a non-null outcome_category next to a row with treatment=1.
Interesting little problem. You can do:
select
outcome_category,
count(x.id) as count
from g
left join (
select distinct id from g where treatment = 1
) x on x.id = g.id
where outcome_category is not null
group by outcome_category
order by count desc
Result:
outcome_category count
----------------- -----
cardiovascular 3
sensory 1
cns 1
metabolic 0
See running example at db<>fiddle.
This would appear to be just a simple aggregation,
select outcome_category, Count(*) count
from t
where treatment=1
group by outcome_category
order by Count(*) desc
Demo fiddle

Max value from joined table

I have two tables:
Operations (op_id,super,name,last)
Orders (or_id,number)
Operations:
+--------------------------------+
|op_id| super| name | last|
+--------------------------------+
|1 1 OperationXX 1 |
|2 1 OperationXY 2 |
|3 1 OperationXC 4 |
|4 1 OperationXZ 3 |
|5 2 OperationXX 1 |
|6 3 OperationXY 2 |
|7 4 OperationXC 1 |
|8 4 OperationXZ 2 |
+--------------------------------+
Orders:
+--------------+
|or_id | number|
+--------------+
|1 2UY |
|2 23X |
|3 xx2 |
|4 121 |
+--------------+
I need query to get table:
+-------------------------------------+
|or_id |number |max(last)| name |
|1 2UY 4 OperationXC|
|2 23X 1 OperationXX|
|3 xx2 2 OperationXY|
|4 121 2 OperationXZ|
+-------------------------------------+
use corelared subquery and join
select o.*,a.last,a.name from
(
select super,name,last from Operations from operations t
where last = (select max(last) from operations t2 where t2.super=t.super)
) a join orders o on t1.super =o.or_id
you can use row_number as well
with cte as
(
select * from
(
select * , row_number() over(partition by super order by last desc) rn
from operations
) tt where rn=1
) select o.*,cte.last,cte.name from Orders o join cte on o.or_id=cte.super
SELECT Orders.or_id, Orders.number, Operations.name, Operations.last AS max
FROM Orders
INNER JOIN Operations on Operations.super = Orders.or_id
GROUP BY Orders.or_id, Orders.number, Operations.name;
I don't have a way of testing this right now, but I think this is it.
Also, you didn't specify the foreign key, so the join might be wrong.

Better way of writing my SQL query with conditional group by

Here's my data
|vendorname |total|
---------------------
|Najla |10 |
|Disney |20 |
|Disney |10 |
|ToysRus |5 |
|ToysRus |1 |
|Gap |1 |
|Gap |2 |
|Gap |3 |
|Najla |2 |
Here's the resultset I want
|vendorname |grandtotal|
---------------------
|Disney |30 |
|Gap |6 |
|ToysRus |6 |
|Najla |2 |
|Najla |10 |
If the vendorname = 'Najla' I want individual rows with their respective total otherwise I would like to group them and return a sum of their totals.
This is my query--
select *
from
(
select vendorname, sum(total) grandtotal
from vendor
where vendorname<>'Najla'
group by vendorname
union all
select vendorname, total grandtotal
from vendor
where vendorname='Najla'
) A
I was wondering if there's a better way to write this query instead of repeating it twice and performing a union. Is there a condensed way to group some rows "conditionally".
Honestly, I think the union all version is going to be the best performing and easiest to read option if it has appropriate indexes.
You could, however, do something like this (assuming you have a unique id on your table):
select vendorname, sum(total) grandtotal
from t
group by
vendorname
, case when vendorname = 'Najla' then id else null end
rextester demo: http://rextester.com/OGZQ33364
returns
+------------+------------+
| vendorname | grandtotal |
+------------+------------+
| Disney | 30 |
| Gap | 6 |
| ToysRus | 6 |
| Najla | 10 |
| Najla | 2 |
+------------+------------+