Join tables, count based on value in column - sql

I have two tables:
Table alpha:
ID | Name
---------
1 | A
2 | B
3 | C
Table beta:
ID | state
----------
1 | 1
1 | 2
1 | 2
2 | 1
I would like to get a result like the following:
ID | Count(state=1) | Count (state=2)
-------------------------------------
1 | 1 | 2
2 | 1 | 0
3 | 0 | 0
How do I do this with SQL?
I can get the table without the result of ID 3, but that is not what I want.
Thanks in advance!

SELECT a.ID,
SUM(CASE WHEN b.state = 1 THEN 1 ELSE 0 END) AS State1Count,
SUM(CASE WHEN b.state = 2 THEN 1 ELSE 0 END) AS State2Count
FROM alpha a
LEFT JOIN beta b
ON a.ID = b.ID
GROUP BY a.ID

Related

SQL to get count of distinct rows based on different rules

Say you have a table like:
| key | status |
| --- | ------ |
| 3 | A |
| 4 | A |
| 4 | C |
| 5 | B |
| 6 | B |
| 6 | C |
| 7 | A |
| 7 | B |
I want a query that returns, in a single row, the count of the number of rows that contain a specific status, but applying some priority rules. The rules would be different for each row and something like:
Column a_count = count of any distinct key that has a status of A
Column b_count = count of any distinct key that has a status of B, but where the same key does not also appear with a status of A
Column c_count = count of any distinct key that has a status of C, but where the same key does not also appear with a status of A or B
The point being that the total of all counts should equal the total number of distinct keys in the source table. In my sample data above, the results should be:
| a_count | b_count | c_count |
| ------- | ------- | ------- |
| 3 | 2 | 0 |
should be able to do your pivot with case statements and not exists.
SELECT Count (CASE
WHEN status = 'A' THEN 1
ELSE 0
END) AS a_count,
Count (CASE
WHEN status = 'B'
AND NOT EXISTS (SELECT 1
FROM mytable b
WHERE a.KEY = b.KEY
AND b.status = 'A') THEN 1
ELSE 0
END) AS b_count,
Count (CASE
WHEN status = 'C'
AND NOT EXISTS (SELECT 1
FROM mytable c
WHERE a.KEY = c.KEY
AND c.status IN ( 'A', 'B' )) THEN 1
ELSE 0
END) AS c_count
FROM mytable a

Expanding information from one row to all similarly grouped rows in SQL

I am not sure of the logic required to accomplish this, but I want to take a table like this...
+----+------+
| Id | Type |
+----+------+
| 10 | A |
| 10 | B |
| 10 | C |
| 20 | A |
| 20 | C |
+----+------+
...and end up with a table like this...
+----+------+---+---+---+
| Id | Type | A | B | C |
+----+------+---+---+---+
| 10 | A | 1 | 1 | 1 |
| 10 | B | 1 | 1 | 1 |
| 10 | C | 1 | 1 | 1 |
| 20 | A | 1 | 0 | 1 |
| 20 | C | 1 | 0 | 1 |
+----+------+---+---+---+
...where each Id will have new columns created to consolidate information about Type into every row of that Id. Since 10 has a row of types A, B, and C, then all rows that have an ID of 10 should have a 1/true in the new columns A, B and C.
I know how to do this on a per-row basis, but can't wrap my head around how to consolidate the information from multiple rows into each row of the same ID.
Try this below logic- Demo
SELECT *,
(SELECT COUNT(DISTINCT Type) FROM your_table B WHERE B.ID = A.Id and B.Type = 'A') A,
(SELECT COUNT(DISTINCT Type) FROM your_table C WHERE C.ID = A.Id and C.Type = 'B') B,
(SELECT COUNT(DISTINCT Type) FROM your_table D WHERE D.ID = A.Id and D.Type = 'C') C
FROM your_table A
And just another option- Demo
SELECT *,
SUM(CASE WHEN Type= 'A' THEN 1 ELSE 0 END) OVER(PARTITION BY Id) A,
SUM(CASE WHEN Type= 'B' THEN 1 ELSE 0 END) OVER(PARTITION BY Id) B,
SUM(CASE WHEN Type= 'C' THEN 1 ELSE 0 END) OVER(PARTITION BY Id) C
FROM your_table

Return all rows from a table and indicate with a new column whether they exist or not in another table

If I have 2 tables:
TABLE_SEARCHFIELDS:
FieldID | FieldName
--------------------
1 | MyField1
2 | MyField2
3 | MyField3
4 | MyField4
5 | MyField5
and
TABLE_CUSTOMSEARCHFIELDS:
UserID | FieldID
--------------------
1 | 1
1 | 2
1 | 5
2 | 2
2 | 4
2 | 5
and I would like to return all of the Searchfields from the first table, but would also like indicated whether that Searchfield is active for a particular user.
E.g. I want to query UserID = 1 and get the result:
FieldID | FieldName | Active
------------------------------
1 | MyField1 | 1
2 | MyField2 | 1
3 | MyField3 | 0
4 | MyField4 | 0
5 | MyField5 | 1
What is the best way to achieve this?
I would do this using exists:
select sf.*,
(case when exists (select 1
from customsearchfields csf
where csf.userid = 1 and csf.fieldid = sf.fieldid
)
then 1 else 0
end) as Active
from searchfields sf;
Assuming you have no duplicate rows, you can also do this using a left join:
select sf.*, (case when csf.userid is not null then 1 else 0 end) as Active
from searchfields sf left join
customsearchfields csf
on csf.userid = 1 and csf.fieldid = sf.fieldid;

SQL Select records excluding some statuses

I'm totally stuck on how to create this select. I need to select from the status table only those order_id's which to not have status 2.
Here is the table:
+----+---------+---------+--
| id | order_id| status |
+----+---------+---------+--
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 2 | 2 |
| 5 | 3 | 1 |
| 1 | 3 | 3 |
| 2 | 4 | 2 |
| 3 | 4 | 1 |
| 4 | 4 | 2 |
| 5 | 5 | 3 |
+----+---------+----------+--
So he select result will be only order_id = 5
Please help!
If you want to include orders with status 1 and exclude those with status 3, then you can use a similar idea:
having sum(case when status_id = 1 then 1 else 0 end) > 0 and
sum(case when status_id = 3 then 1 else 0 end) = 0
EDIT: I like to EXCLUDE those order_id's:
- Which has only status 1 (not status 2)
- and
- which has status 3
Lets have table like this:
id--order-id-Prod---Status
------------------------------
1 1 a 1
6 1 b 2
7 1 a 2
8 1 b 1
9 2 a 1
10 3 a 1
11 3 b 1
12 3 a 2
13 3 b 2
14 4 a 1
15 4 b 1
16 5 a 1
17 5 b 1
18 5 a 2
19 5 b 2
20 5 a 3
21 5 b 3
Select should show only order_id "5"
This is an example of a set-within-sets query:
select order_id
from t
group by order_id
having sum(case when status = 2 then 1 else 0 end) = 0
The having clause counts the number of rows with a status of 2. The = 0 finds the orders with no matches.
EDIT:
If you want to include orders with status 1 and exclude those with status 3, then you can use a similar idea:
having sum(case when status_id = 1 then 1 else 0 end) > 0 and
sum(case when status_id = 3 then 1 else 0 end) = 0
Here's one way.
Select * from TableName
where Order_ID not in (Select order_ID from tableName where status=2)
Another way would be to use the not exists clause.
Another way is to use EXCEPT:
SELECT order_id
FROM StatusTable
EXCEPT
SELECT order_id
FROM StatusTable
WHERE status = 2;
It works in SQL-Server and Postgres (and in Oracle if you replace the EXCEPT with MINUS.)
I think this works, one query to select all ids, one to get those with a status of 2 and left joining on order_id and picking those with null order_id in the list of orders with a status of 2.
select
all_ids.order_id
from
(
select distinct
order_id
from status
) all_ids
left join
(
select
order_id
from status
where status = 2
) two_ids
on all_ids.order_id = two_ids.order_id
where two_ids.order_id is null

SQL query handling three tables

I want to compile the data for reporting purpose, using php communicating with postgres,
I have three tables
product_status:
status_code | value
------------|------
1 | a
2 | b
product_child:
code| ID | status_code
----|----|------------
X | 1 | 1
X | 2 | 1
X | 3 | 2
Y | 1 | 2
Y | 2 | 2
Z | 1 | 1
product_master:
code|description(and some other columns not relevent)
X | la
Y | alb
Z | lab
In the end I want basically a table like this which i'll display
| total child | status a | status b
bla | 3 | 2 | 1
alb | 2 | 0 | 2
lab | 1 | 1 | 0
I have tried
SELECT s.value, count(s.value)
FROM product_child p, product_status s
WHERE
p.product_status = s.status_code
and p.product_code = get_product_code('Sofa (1-seater) J805')
group by
s.value
it gives we grouping for particular code but I want this flipped and appended in front of distinct product_codes
do you mean like this?
select pm.description,
count(pc.code) total,
count(case when ps.value = 'A' then ps.value else null end) statusA,
count(case when ps.value = 'B' then ps.value else null end) statusB
from product_master pm join product_child pc on pm.code = pc.code
join product_status ps on pm.status_code = ps.status_code
group by pm.description