postgres array_agg with distinct on json_build_object - json

select
t1.id,
array_agg(
json_build_object('id', t2.id, 'status', t2.status)
) as statuses
from table1 t1
inner join table2 t2 on t1.id=t2.user_id
inner join table3 t3 on t1.id=t3.user_id
group by t1.id
table1
id , user
1 , 'A'
2 , 'B'
table2
user_id , status
1 , 'P'
1 , 'AP'
table3
user_id , something
1 , 'A12'
1 , 'B1212'
the table3 also one-many relationship as a result the duplication of statuses coming in the array_agg,
i tried with array_agg(distinct json_build_object()) and array_agg(distinct on json_build_object()),
how can we prevent duplications in this case ??

Just filter out the relevant status as join condition (1):
select
t1.id,
array_agg(
json_build_object('id', t2.id, 'status', t2.status)
) as statuses
from table1 t1
inner join table2 t2 on t1.id=t2.user_id
inner join table3 t3 on t1.id=t3.user_id
and t3.status = 'A12' -- 1.
group by t1.id
Furthermore, if you want to get valid JSON arrays, you should use json_agg() instead of array_agg():
select
t1.id,
json_agg(
json_build_object('id', t2.id, 'status', t2.status)
) as statuses
from table1 t1
inner join table2 t2 on t1.id=t2.user_id
inner join table3 t3 on t1.id=t3.user_id
and t3.status = 'A12'
group by t1.id

Related

MySQL - Where occurs at least 3 times

I have got this query, which works fine:
SELECT t1.*, t2.ip as ip
FROM table1 t1
INNER JOIN table2 t2 ON ( t2.id = t1.t2id )
ORDER BY t1.timestamp DESC
LIMIT 1000
What I would like to do is to:
1) get only those entries where the same ip occurs at least 3 times
2) group the entries by ip
So the result would look like this example:
IP TIMESTAMP
111.111.111.111 1500000000
111.111.111.111 1300000000
111.111.111.111 1100000000
222.222.222.222 1400000000
222.222.222.222 1300000000
222.222.222.222 1200000000
I have tried many approaches and I believe that this one is the closest,
but the result is 0 rows.
SELECT *, COUNT(DISTINCT ip) FROM (
SELECT t1.*, t2.ip as ip
FROM table1 t1
INNER JOIN table2 t2 ON ( t2.id = t1.t2id )
ORDER BY t1.timestamp DESC
LIMIT 1000
) AS tmp_table
GROUP BY ip
HAVING COUNT(DISTINCT ip) > 2
Please can someone shine some light on this?
Try this:
SELECT t1.*, (SELECT DISTINCT t2.ip FROM t2 WHERE t2.id = t1.t2id)
FROM t1
WHERE
(SELECT COUNT(*)
FROM t2
WHERE t2.id = t1.t2id) >= 3
Bacause in the comments has resulted table t2 with more rows for the same IP I change my query as follow:
SELECT t1.*, t2.ip
FROM t1
JOIN t2
ON t2.id = t1.t2id
WHERE
(SELECT COUNT(*)
FROM t2 tt2
WHERE t2.ip = tt2.ip) >= 3
You can see SqlFiddle
SELECT *, COUNT(ip) FROM (
SELECT t1.*, t2.ip as ip
FROM table1 t1
INNER JOIN table2 t2 ON ( t2.id = t1.t2id )
ORDER BY t1.timestamp DESC
LIMIT 1000
) AS tmp_table
GROUP BY ip
HAVING COUNT(ip) > 2
just remove distinct
You have to have the HAVING in the subquery
SELECT t1.*, t2.ip as ip
FROM table1 t1
INNER JOIN table2 t2 ON ( t2.id = t1.t2id )
INNER JOIN
(
SELECT t2.ip
FROM table1 t1
INNER JOIN table1 t2 ON ( t2.id = t2.t2id )
GROUP BY t2.ip
HAVING count(ip) > 2
) t ON t2.ip = t.ip
Try like this;
SELECT t2.ip as ip
FROM table1 t1
INNER JOIN table2 t2 ON ( t2.id = t1.t2id )
where t2.ip IN (SELECT ip FROM (
SELECT t2.ip as ip
FROM table1 t1
INNER JOIN table2 t2 ON ( t2.id = t1.t2id )
) AS tmp_table
GROUP BY ip
HAVING COUNT(*) > 2)
ORDER BY t1.timestamp DESC
LIMIT 1000

MySQL writing join on three tables

I have written the following mysql query
SELECT distinct name, date
FROM table1
JOIN table2 ON (table2.item_id = table1.item_id)
where table1.id IN (SELECT run_id FROM
table3 where table3.status = 'FAIL') and table1.createdate >= '2011-01-01'
;
I want to include another field message from table3 in my output.
I guess this might involve joining 3 tables in the query.
Please let me know how to achieve this.
There's not quite enough info to fully answer the question. We also want to know:
Does the status column come from table1 or from table3
Can there be more than one table3 record with a runid that matches a table1.id value?
If there can be, which table3.message value do you want to show?
Without knowing that info, all we can do is guess. Here is my guess:
SELECT distinct name, date, message
FROM table1 t1
INNER JOIN table2 t2 ON t2.item_id = t1.item_id
INNER JOIN table3 t3 on t1.id =t3.run_id AND t3.status = 'FAIL'
WHERE t1.createdate >= '2011-01-01'
Try this :-
SELECT DISTINCT T1.NAME , T1.DATE , T3.MESSAGE
FROM TABLE1 T1
JOIN TABLE2 T2
ON
T1.ITEM_ID = T2.ITEM_ID
INNER JOIN TABLE3 T3
ON
T1.ID = T3.RUN_ID
WHERE T3.STATUS = 'FAIL' AND T1.CREATEDATE >= '2011-01-01';
Try this
SELECT distinct name, date, T3.message
FROM table1
JOIN table2 ON (table2.item_id = table1.item_id)
INNER JOIN (SELECT run_id FROM
table3 where status = 'FAIL') T3 ON T3.run_id = table1.id
where table1.createdate >= '2011-01-01'
try this :
select distinct name,date, message from table1 t1,table2 t2,table3 t3 where t1.item_id = t2.item_id and t1.item_id = t3.item_id
and t3.status ='FAIL' AND t1.createdate >= to_date('2011-01-01,'YYYY-DD-MM');

Mysql : Left Join and Inner Join in a subquery

I have 3 tables that i have left joined but I would like to add a field from the table 3 which is inner joined with table 2.
Table1
id
name
surname
table2_fk
Table2
id
entry_name
entry_code
table3_fk
Table3
id
type_name
type_desc
SELECT `Name`, `Type Description`
(SELECT
Table1.name AS `Name`,
Table1.surname AS `Surname`,
t2.entry_name AS `Entry`,
t2.entry_code AS `Entry Code`,
t3.type_name AS `Type Name`,
t3.type_desc AS `Type Description`
FROM Table1
LEFT JOIN table2 AS t2 ON Table1.table2_fk = t2.id
LEFT JOIN table3 AS t3 ON Table2.table3_fk = t3.id
) as subquery
My desired outcome is to have t2 inner join t3 and get field from table 1 and table 3
Based on my subquery I would like the Name and Type Description to show
If you want to INNER JOIN tables table2 and table3 and then LEFT JOIN to table1 then do this:
SELECT t1.name, t.type_desc AS `Type Description`
FROM Table1 AS t1
LEFT JOIN (
SELECT t2.id, t3.type_desc
FROM table2 AS t2
INNER JOIN table3 AS t3 ON t2.table3_fk = t3.id
) AS t ON t1.table2_fk = t2.id
But this could be perhaps expressed just as:
SELECT t1.name, t3.type_desc AS `Type Description`
FROM Table1 AS t1
LEFT JOIN table2 AS t2 ON t1.table2_fk = t2.id
LEFT JOIN table3 AS t3 ON t2.table3_fk = t3.id
I presume you are trying to left join table1 with the result of inner join between table2 and table3
select t1.name, t_res.type_desc from table1 t_1
left join (
select t_2.id, t_3.type_desc from table2 t_2
inner join table3 t_3 on t_2.table3_fk = t_3.id
) as t_res on t_1.table2_fk = t_2.id

SQL trouble with COUNT

I have some SQL code that returns me some data from DB
SELECT t1.id as id, title, description FROM table1 t1
JOIN table2 t2 ON t1.id = t2.t1_id
WHERE t2.t3_id IN( SELECT id FROM table3 WHERE parent_id IN ( SELECT id FROM table3 WHERE parent_id = 1)) GROUP BY t1.id
I have some problem with counting number of rows of result. I know that I have to write almost the same code but with COUNT but I have there A problem, my code doesn't return me a number of rows.
Just use the COUNT(*) function. Also, your subqueries can be converted to a JOIN (and your sub-subquery is redundant):
SELECT COUNT(*)
FROM table1 t1
JOIN table2 t2
ON t1.id = t2.t1_id
JOIN table3 t2
ON t3.id = t2.t3_id
WHERE t3.parent_id = 1

Mysql: ignore 2 distinct columns from a table

I have 2 tables that are something like this:
Table1:
animal name color
cat bob red
dog bill blue
Table2:
animal name
cat bob
fish joe
I want to query all the data in table 1 except if they have the unique identity from table 2, so I would get dog bill blue, and not the cat, since it appears in table 2.
I did:
select t.animal, t.name, t.color
from Table1 as t, Table2 as t2
where NOT(t.animal = t2.animal AND
t.name = t2.name)
group by t.animal, t.name
Is there a more efficient way of doing this?
select t.animal, t.name, t.color
from Table1 as t
LEFT JOIN Table2 as t2 ON t.animal = t2.animal and t.name = t2.name
where table2.name is null
group by t.animal, t.name
A simple where not exists clause should do the trick:
select t1.animal, t1.name, t1.color
from Table1 t1
where not exists
(
select 1
from Table2 t2
where t2.animal = t1.animal
and t2.name = t1.name
)
You can also use the anti-join pattern:
select t1.animal, t1.name, t1.color
from Table1 t1
left outer join Table2 t2 on t1.animal = t2.animal and t1.name = t2.name
where t2.animal is null
SELECT * FROM table_one t1
WHERE t1.animal NOT IN
(SELECT animal FROM table_two)