I would like my query to return a result structured like this, where tags is an array of arrays or similar:
id | name | tags
1 a [[1, "name1", "color1"], [2, "name2", color2"]]
2 b [[1, "name1", "color1"), (3, "name3", color3"]]
I expected this query to work, but it gives me an error:
SELECT i.id, i.name, array_agg(t.tag_ids, t.tag_names, t.tag_colors) as tags
FROM ITEMS
LEFT OUTER JOIN (
SELECT trm.target_record_id
, array_agg(tag_id) as tag_ids
, array_agg(t.tag_name) as tag_names
, array_agg(t.tag_color) as tag_colors
FROM tags_record_maps trm
INNER JOIN tags t on t.id = trm.tag_id
GROUP BY trm.target_record_id
) t on t.target_record_id = i.id;
Error:
PG::UndefinedFunction: ERROR: function array_agg(integer[], character varying[], character varying[]) does not exist
LINE 1: ..., action_c2, action_c3, action_name, action_desc, array_agg(...
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
This query works and produces similar results (but not quite what I want):
SELECT i.id, i.name, t.tag_ids, t.tag_names, t.tag_colors as tags as tags
FROM ITEMS
LEFT OUTER JOIN (
SELECT trm.target_record_id, array_agg(tag_id) as tag_ids, array_agg(t.tag_name) as tag_names, array_agg(t.tag_color) as tag_colors
FROM tags_record_maps trm
INNER JOIN tags t on t.id = trm.tag_id
GROUP BY trm.target_record_id
) t on t.target_record_id = i.id;
Result:
id | name | tag_ids | tag_names | tag_colors
1 a [1, 2] ["name1, "name2"] ["color1", "color2"]
1 a [1, 3] ["name1, "name3"] ["color1", "color3"]
Edit:
This query almost produces what I'm looking for, except it names the json keys f1, f2, f3. It would be perfect if I could name them id, name, color:
SELECT trm.target_record_id, json_agg( (t.id, t.tag_name, t.tag_color) )
FROM tags_record_maps trm
INNER JOIN tags t on t.site_id = trm.site_id and t.id = trm.tag_id
GROUP BY trm.target_record_id
having count(*) > 1;
Result:
[{"f1":1,"f2":"name1","f3":"color1"},{"f1":2,"f2":"name2","f3":"color2"}]
(t.id, t.tag_name, t.tag_color) is short syntax for ROW(t.id, t.tag_name, t.tag_color) - and a ROW constructor does not preserve nested attribute names. The manual:
By default, the value created by a ROW expression is of an anonymous record type. If necessary, it can be cast to a named composite type —
either the row type of a table, or a composite type created with
CREATE TYPE AS.
Bold emphasis mine. To also get proper key names in the result, cast to a registered composite type as advised in the quote, use a nested subselect, or simply use json_build_object() in Postgres 9.4 or newer (effectively avoiding the ROW constructor a priori):
SELECT trm.target_record_id
, json_agg(json_build_object('id', t.id
, 'tag_name', t.tag_name
, 'tag_color', t.tag_color)) AS tags
FROM tags_record_maps trm
JOIN tags t USING (site_id)
WHERE t.id = trm.tag_id
GROUP BY trm.target_record_id
HAVING count(*) > 1;
I use original column names, but you can chose your key names freely. In your case:
json_agg(json_build_object('id', t.id
, 'name', t.tag_name
, 'color', t.tag_color)) AS tags
Detailed explanation:
Return multiple columns of the same row as JSON array of objects
array_agg() puts one argument into an array. You could try to concatenate the values together:
array_agg(t.tag_ids || ':' || t.tag_names || ':' || t.tag_colors)
Or perhaps use a row constructor:
array_agg( (t.tag_ids, t.tag_names, t.tag_colors) )
Why not try a Json_Agg()?
SELECT
json_agg(tag_ids, tag_names, tag_colors)
FROM items
Etc...
DB fiddle
let's play with composite type.
create type tags as(tag_id bigint, tag_name text,tag_color text);
using array_agg:
select item_id,name, array_agg(row(trm.tag_id, tag_name, tag_color)::tags) as tags
from items i join tags_record_maps trm on i.item_id = trm.target_record_id
group by 1,2;
to json.
select item_id,name, to_json( array_agg(row(trm.tag_id, tag_name, tag_color)::tags)) as tags
from items i join tags_record_maps trm on i.item_id = trm.target_record_id
group by 1,2;
access individual/base element of composite type:
with a as(
select item_id,name, array_agg(row(trm.tag_id, tag_name, tag_color)::tags) as tags
from items i join tags_record_maps trm on i.item_id = trm.target_record_id
group by 1,2)
select a.item_id, a.tags[2].tag_id from a;
I have two tables named [DrugPrescriptionEdition] and [PrescriptionDoseDetail] and now, I join that two tables using the below query and taking a result set.
select * from DrugPrescription dp where id in(
SELECT distinct dpe.template
FROM [DrugPrescriptionEdition] dpe
join PrescriptionDoseDetail pdd on pdd.prescription = dpe.id
where doseEnd_endDate is NULL and doseEnd_doseEndType =1
)
but now I want to take records only contain, (1,2) combination of 'datasource' column and prescription.id should be same.
Example : like records { prescriptionID =4 and there contain ,(1,2) }. I will not consider, only 1 ,or 2 contain records.
Need some expert help to adding this conditions to my above query and modify it .
Expected result : I need to filter out , above query result using this, new condition too.
Let me assume your records are in a single table. Here is one method:
select t.*
from t
where (t.dataSource = 1 and
exists (select 1
from t t2
where t2. prescriptionid = t.prescriptionid and
t2.dataSource = 2
)
) or
(t.dataSource = 2 and
exists (select 1
from t t2
where t2.prescriptionid = t.prescriptionid and
t2.dataSource = 2
)
);
It is unclear if any other data sources are allowed. If they are not, then add:
and
not exists (select 1
from t t3
where t3.prescriptionid = t.prescriptionid and
t3.dataSource not in (1, 2)
)
I need to find the key of the minimum value in a jsonb object,I have found out minimum value, need to find the key of the same in the same query.
Query I am using
SELECT id,min((arr ->> 2)::numeric) AS custom_value
FROM (
SELECT id, jdoc
FROM table,
jsonb_each(column1) d (key, jdoc)
) sub,
jsonb_each(jdoc) doc (key, arr)
group by 1
This will do the job.
The left join ... on 1=1 is for keeping IDs with empty json
select t.id
,j.key
,j.value
from mytable t
left join lateral (select j.key,j.value
from jsonb_each(column1) as j
order by j.value
limit 1
) j
on 1=1
I'm trying to make an sql request with join exclusion.
Explains:
Table element
id # name #
1 Sea
2 tree
Table colour
id # name #
1 green
2 blue
3 brown
Table relation
element_id # colour_id
1 2
2 1
2 3
I have my working request for "get elements for one of these colours".
Exemple with green and blue:
SELECT element.name, colour.name FROM element
LEFT JOIN relation
ON (element.id = relation.element_id)
LEFT JOIN colour
ON (colour.id = relation.colour_id)
WHERE (relation.colour_id = 1 OR relation.colour_id = 2)
I would like make request for "get elements where they have a relation with all listed colors". Where for green and brown it returns tree.
I've tried to change the 'OR' to 'AND' but request return 0 results :/
General way to solve this problem is to filter values and count how many times they appear in result. If equal, all elements are found.
select element_id
from relation
where colour_id in (1, 2)
group by element_id
having count (distinct colour_id) = 2
Having this table one might join it to original tables to produce full column set:
SELECT element.name, colour.name
FROM relation
INNER JOIN
(
select element_id
from relation
where colour_id in (1, 2)
group by element_id
having count (distinct colour_id) = 2
) matches
ON relation.element_id = matches.element_id
INNER JOIN element
ON element.id = relation.element_id
INNER JOIN colour
ON colour.id = relation.colour_id
This type of query can be handled with some of SQL's set-based operators:
Which elements have relations for all colours?
Using the ALL operator (syntax may vary slightly by database):
SELECT element.name
FROM element
WHERE ( SELECT colour.id FROM relation
INNER JOIN colour ON colour.id = relation.colour_id
WHERE relation.element_id = element.id )
= ALL ( SELECT colour.id from colour)
;
Using the EXCEPT operator:
SELECT element.name
FROM element
WHERE NOT EXISTS
( SELECT colour.id from colour
EXCEPT
SELECT colour.id FROM relation
INNER JOIN colour ON colour.id = relation.colour_id
WHERE relation.element_id = element.id
)
;
There is a typo in your WHERE clause - one of the ids is 2 ("blue") instead of 3 ("brown"). It should be
WHERE (relation.colour_id = 1 OR relation.colour_id = 3)
(or in shorter form:
WHERE relation.colour_id IN (1, 3)
).
Note however, that your current query - although after this fix it should work for your sample data - won't give you correct results in general. It will give you the elements associated with any of the specified colors. The correct solution to this is given in #Nikola's answer though.
without subselects, I would suggest:
SELECT
e.id AS id
FROM
element AS e
LEFT OUTER JOIN relation AS r ON r.element_id = e.id
GROUP BY e.id HAVING SUM(CASE WHEN r.colour_id = 1 THEN 1 ELSE 0 END ) = 0
ORDER BY e.id ASC;
after this you can select the elements by id.
excuse the title, i couldn't come up with something short and to the point...
I've got a table 'updates' with the three columns, text, typeid, created - text is a text field, typeid is a foreign key from a 'type' table and created is a timestamp. A user is entering an update and select the 'type' it corresponds too.
There's a corresponding 'type' table with columns 'id' and 'name'.
I'm trying to end up with a result set with as many rows as is in the 'type' table and the latest value from updates.text for the particular row in types. So if i've got 3 types, 3 rows would be returned, one row for each type and the most recent updates.text value for the type in question.
Any ideas?
thanks,
John.
select u.text, u.typeid, u.created, t.name
from (
select typeid, max(created) as MaxCreated
from updates
group by typeid
) mu
inner join updates u on mu.typeid = u.typeid and mu.MaxCreated = u.Created
left outer join type t on u.typeid = t.typeid
What are the actual columns you want returned?
SELECT t.*,
y.*
FROM TYPE t
JOIN (SELECT u.typeid,
MAX(u.created) 'max_created'
FROM UPDATES u
GROUP BY u.typeid) x ON x.typeid = t.id
JOIN UPDATES y ON y.typeid = x.typeid
AND y.created = x.max_created
SELECT
TYP.id,
TYP.name,
TXT.comment
FROM
dbo.Types TYP
INNER JOIN dbo.Type_Comments TXT ON
TXT.type_id = TYP.id
WHERE
NOT EXISTS
(
SELECT
*
FROM
dbo.Type_Comments TXT2
WHERE
TXT2.type_id = TYP.id AND
TXT2.created > TXT.created
)
Or:
SELECT
TYP.id,
TYP.name,
TXT.comment
FROM
dbo.Types TYP
INNER JOIN dbo.Type_Comments TXT ON
TXT.type_id = TYP.id
LEFT OUTER JOIN dbo.Type_Comments TXT2 ON
TXT2.type_id = TYP.id AND
TXT2.created > TXT.created
WHERE
TXT2.type_id IS NULL
In either case, if the created date can be identical between two rows with the same type_id then you would need to account for that.
I've also assumed at least one comment per type exists. If that's not the case then you would need to make a minor adjustment for that as well.