PostgreSQL group by then count by value - sql

I have the following data
id | sub_id |status |
---|--------|-------|
1 | 1 | new |
2 | 2 | old |
3 | 2 | new |
4 | 3 | old |
Which query should I use to get the following result?
I want to group the result by sub_id and then add new columns that store the number of statuses of the corresponding sub_id.
sub_id | new | old | total |
-------|------|------|-------|
1 | 1 | 0 | 1 |
2 | 1 | 1 | 2 |
3 | 0 | 1 | 1 |
I tried this and it did not work as expected.
SELECT
sub_id,
count(status='new') AS new,
count(status='old') AS old,
count(status) AS total
FROM table
GROUP BY sub_id;

status = 'new' is
true for all rows with status = 'new'
false for all rows with status <> 'new'
null for all rows with status is null.
COUNT( <expression> ) counts all non-null occurences of the expression. This means you count both 'new' and 'old', as neither true nor false is null, when you only want to count 'new'. Use a CASE expression instead:
count(case when status = 'new' then 1 end)
which is short for
count(case when status = 'new' then 1 else null end)
or the same with SUM:
sum(case when status = 'new' then 1 else 0 end)
Some DBMS (MySQL for instance) treat true as 1 and false as 0. There you can even use:
sum(status = 'new')
In PostgreSQL you can also use the filter() clause:
count(*) filter (where status = 'new')

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

SQL- count the non NULL values and count the rows that has string "1"

I'm trying to count non null row in a column but it's counting all the rows and and count the rows in a column that has string "1".
I was able to count the rows in a column that has string "1" for the 1st column but on the 2nd one, it's count the "0" too.
I've seen some articles here but it didn't resolved the issue.
SELECT NAME as Agent_Name, COUNT(case when Thumbs_Up= 1 then 1 else null end) as Thumbs_Up,
COUNT(case when No_Solution_Found =1 then 1 else null end) as No_Solution,
COUNT(case when Save is null then 0 else 1 end) as Total_Saves,
FROM table
GROUP BY NAME
Table:
Name | Thumbs_up | No_Solution_Found | Save
Jonathan | 1 | 0 | Saved
Mike | 0 | 1 | Null
Peter | 1 | 0 | Null
Mike | 1 | 0 | Saved
Peter | 0 | 1 | Saved
Mike | 1 | 0 | Saved
Peter | 0 | 1 | Saved
Expected results:
Name | Thumbs_up | No_Solution | Total_Save
Jonathan | 1 | 0 | 1
Mike | 2 | 1 | 2
Peter | 1 | 2 | 2
Try with SUM instead of COUNT
SELECT NAME as Agent_Name,
SUM(case when Thumbs_Up = 1 then 1 else 0 end) as Thumbs_Up,
SUM(case when No_Solution_Found =1 then 1 else 0 end) as No_Solution,
SUM(case when Save is null then 0 else 1 end) as Total_Saves,
FROM table
GROUP BY NAME
Since only the Save column has NULLs, I assume that's the column you have the problem with.
In your query you wrote:
COUNT(case when Save is null then 0 else 1 end) as Total_Saves,
That is, you're replacing NULL by 0, which is a non null value and therefore is counted.
You presumable wanted to just write:
COUNT(Save) as Total_Saves
(And BTW, there is a comma after as Total_Saves in your query, that doesn't belong there, as no other column expression follows.)
Try the following query-:
Select
Name,
sum(Thumbs_up),
sum(No_Solution_Found),
count(case when [Save] is not null then 1 else null end) as Total_save
from TABLE
group by Name
SQL Server 2014

SQL Grouping entries with a different value

Let's assume I have a report that displays an ID and VALUE from different tables
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 1 | 0 |
3 | 1 | 1 |
4 | 2 | 0 |
5 | 2 | 0 |
My goal is to display this table with grouped IDs and VALUEs. My rule to grouping VALUEs would be "If VALUE contains atleast one '1' then display '1' otherwise display '0'".
My current SQL is (simplified)
SELECT
TABLE_A.ID,
CASE
WHEN TABLE_B.VALUE = 1 OR TABLE_C.VALUE NOT IN (0,1,2,3)
THEN 1
ELSE 0
END AS VALUE
FROM TABLE_A, TABLE_B, TABLE_C
GROUP BY
TABLE_A.ID
(CASE
WHEN TABLE_B.VALUE = 1 OR TABLE_C.VALUE NOT IN (0,1,2,3)
THEN 1
ELSE 0
END)
The output is following
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 1 | 0 |
3 | 2 | 0 |
Which is half way to the output I want
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 2 | 0 |
So my Question is: How do I extend my current SQL (or change it completely) to get my desired output?
If you are having only 0 and 1 as distinct values in FOREIGN_VALUE column then using max() function as mentioned by HoneyBadger in the comment will fulfill your requirement.
SELECT
ID,
MAX(FOREIGN_VALUE) AS VALUE
FROM (SELECT
ID,
CASE WHEN FOREIGN_VALUE = 1
THEN 1
ELSE 0
END AS FOREIGN_VALUE
FROM TABLE,
FOREIGN_TABLE)
GROUP BY
ID;
Assuming value is always 0 or 1, you can do:
select id, max(value) as value
from t
group by id;
If value can take on other values:
select id,
max(case when value = 1 then 1 else 0 end) as value
from t
group by id;

SQL Pivot using count

I have a table which has the following entries
ID | column | value
------------------------
1 | status | DONE
2 | status | FAILED
1 | progress | Green
2 | progress | Red
i want the output as
ID | DONE | FAILED | GREEN | RED
1 | 1 | 0 | 1 | 0
2 | 0 | 1 | 0 | 1
Please let me know the query. I have tried pivot but not getting the results.
Here is a standard pivot query solution which does not use SQL Server's built in PIVOT capability:
SELECT ID,
SUM(CASE WHEN value = 'DONE' THEN 1 ELSE 0 END) AS DONE,
SUM(CASE WHEN value = 'FAILED' THEN 1 ELSE 0 END) AS FAILED,
SUM(CASE WHEN value = 'Green' THEN 1 ELSE 0 END) AS GREEN,
SUM(CASE WHEN value = 'Red' THEN 1 ELSE 0 END) AS RED
FROM yourTable
GROUP BY ID
SELECT *
FROM atable
PIVOT (
COUNT(column)
FOR value in ([DONE], [FAILED], [GREEN], [RED])
) p

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;