Counting distinct values within group - sql

Given a table like so:
| JobId | Result |
|-------|--------|
| 1 | true |
| 1 | false |
| 1 | true |
| 1 | |
| 2 | false |
| 2 | false |
| 1 | true |
| 1 | true |
| 1 | true |
Is it possible for a SQL query to generate an output like this?
[{JobId: 1, true: 2, false: 1, undefined: 1},
{JobId: 2, true: 3, false: 2, undefined: 0}]
I am using the ORM Sequelize currently, but can use raw queries, this is what I have so far.
db.JobResponse.findAll({
where: {
jobId: job
},
attributes: ['JobId', [fn('sum', col('result'))]],
group: ['JobId']
}).then(result => {
res.status(200).send({
data: result
});
})
Currently it is trying to sum the result column, grouping by JobId, however, result is a boolean (expected value are true, false and undefined), so a simple sum won't work. Is it possible to count per distinct value within a group?

I think a basic pivot query to aggregate the true, false, and undefined tallies along with FOR JSON AUTO at the end of the query should generate the result you want:
SELECT
JobId,
SUM(CASE WHEN Result = 'true' THEN 1 ELSE 0 END) AS true,
SUM(CASE WHEN Result = 'false' THEN 1 ELSE 0 END) AS false,
SUM(CASE WHEN Result IS NULL THEN 1 ELSE 0 END) AS undefined
FROM yourTable
GROUP BY JobId
FOR JSON AUTO; -- convert each result record to a JSON element inside an outer array []
I couldn't actually test this, because both Rextester and SQLFiddle appear to not support the SQL Server JSON extensions. But this useful tutorial seems to support the answer I gave.

Related

SQL : get values from column depending on boolean expressions in a column

I have a query result like this:
|bool Expression | Column A | Column B|
+----------------+----------+---------+
| true | 2 | 10 |
| false | 3 | 10 |
| true | 4 | 8 |
I need all values of Column B where all boolean expressions from A are true.
The Result I need in this case would be [8] if all were true it would be [8, 10]
Thanks in advance
You can group by columnb:
select columnb
from tablename
group by columnb
having min(boolexpression::int) = 1 and max(boolexpression::int) = 1
I would simply use boolean aggregation functions:
select b
from t
group by b
having bool_and(bool_expression);
As an aside, this will treat NULL boolean expressions correctly -- that is, the b value will be filtered out.
Why is the following not the solution
select b
from table
where expression = true

Filter json values regardless of keys in PostgreSQL

I have a table called diary which includes columns listed below:
| id | user_id | custom_foods |
|----|---------|--------------------|
| 1 | 1 | {"56": 2, "42": 0} |
| 2 | 1 | {"19861": 1} |
| 3 | 2 | {} |
| 4 | 3 | {"331": 0} |
I would like to count how many diaries having custom_foods value(s) larger than 0 each user have. I don't care about the keys, since the keys can be any number in string.
The desired output is:
| user_id | count |
|---------|---------|
| 1 | 2 |
| 2 | 0 |
| 3 | 0 |
I started with:
select *
from diary as d
join json_each_text(d.custom_foods) as e
on d.custom_foods != '{}'
where e.value > 0
I don't even know whether the syntax is correct. Now I am getting the error:
ERROR: function json_each_text(text) does not exist
LINE 3: join json_each_text(d.custom_foods) as e
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
My using version is: psql (10.5 (Ubuntu 10.5-1.pgdg14.04+1), server 9.4.19). According to PostgreSQL 9.4.19 Documentation, that function should exist. I am so confused that I don't know how to proceed now.
Threads that I referred to:
Postgres and jsonb - search value at any key
Query postgres jsonb by value regardless of keys
Your custom_foods column is defined as text, so you should cast it to json before applying json_each_text. As json_each_text by default does not consider empty jsons, you may get the count as 0 for empty jsons from a separate CTE and do a UNION ALL
WITH empty AS
( SELECT DISTINCT user_id,
0 AS COUNT
FROM diary
WHERE custom_foods = '{}' )
SELECT user_id,
count(CASE
WHEN VALUE::int > 0 THEN 1
END)
FROM diary d,
json_each_text(d.custom_foods::JSON)
GROUP BY user_id
UNION ALL
SELECT *
FROM empty
ORDER BY user_id;
Demo

use default value in partition clause of a window function if null

I have used window functions in my query to sum my rows according to value in combination of rows. Now If 1 row contains null then I have to consider it as false what should i do? I had tried adding coalesce(atg.flag,false) in partition but it didn't work.
coalesce is the way, here is an example:
t=# with dset(i,bool) as (values(1,true),(2,false),(3,null))
select i, bool::text, count(1) over (partition by coalesce(bool,false))
from dset;
i | bool | count
---+-------+-------
2 | false | 2
3 | | 2
1 | true | 1
(3 rows)
as you can see count =2 for null and false and =1 for true

sql: check for existence of only one specific item for an id, when multiple items may exist

I have a database of locations that can have anywhere from 0 to 27 types of logs available. I'm only interested in the presence of 1 type of log and want to return true or false if that log exists. I tried using CASE WHEN but the problem is my results are showing true or false if the one log exists (which is the desired outcome)and then false for all of the other logs, so I get multiple rows with the same id (which is not what I want).
Sample code:
SELECT
Table1.LocationId
, CASE WHEN Table2.LogId = 8 THEN 'True' ELSE 'False' END AS 'Log8Avail'
Desired result would be:
| LocationID |Log8Avail|
----------------------
| 1 | True |
----------------------
| 2 | False |
----------------------
| 3 | True |
----------------------
But what I'm currently getting is:
| LocationId | Log8Avail|
-----------------------
| 1 | True |
-----------------------
| 1 | False |
-----------------------
| 1 | False |
-----------------------
| 2 | False |
-----------------------
My question is, how can I adjust my query so that it only looks for the presence or absence of the specific log I'm interested in?
SELECT t1.LocationId,
CASE WHEN t2.LogID IS NULL
THEN false
ELSE TRUE
END as Log8Avail
FROM Table1 t1
LEFT JOIN Table2 t2
ON t1.id = t2.id
AND t2.LogId = 8
SELECT
Table1.LocationId
, CASE WHEN (SELECT COUNT(*)
FROM Table2
WHERE Table2.LogId = 8)>0 THEN 'True' ELSE 'False'
END AS 'Log8Avail'
Like this?

Unique values in Sequelize

I use Sequelize ORM for Node.js. I need to get lines with unique values in certain column. Example table:
id | name | group
-----------------
1 | One | 2
2 | Two | 1
3 | Three| 2
4 | Four | 3
5 | Five | 1
Query for column group and result:
id | name | group
-----------------
1 | One | 2
2 | Two | 1
4 | Four | 3
Lines One, Two and Four was the first who had unique group values. How to make it in Sequelize?
A Sequelize raw query is one way of getting out the rows that you want:
/*
var sql =
SELECT r.id,
r.name,
r.groupnum
FROM s04.row r
JOIN
(SELECT min(id) AS id,
groupnum
FROM s04.row
GROUP BY groupnum) s
ON r.id = s.id
*/
return sq.query(sql, { type: sq.QueryTypes.SELECT});
The resulting promise will resolve to a JSON array:
[
{
"id": 1,
"name": "One",
"groupnum": 2
}
...
]
If you then needed to work with these rows as Instances you can call build on each element of the array:
Model.build({ /* attributes-hash */ }, { isNewRecord: false })
See here for an example. If I find a way of doing this via Sequelize function calls (aggregate, find*, etc) that isn't too hideous I'll also post that here as a separate answer.