How to apply ORDER BY for more than two column in mysql? - mysql-workbench

Below query,I try to sort(ascending) the result by reviewer name, movie title and then by review stars.
The query I written sort only the reviewer name and movie title only not all the three.
mysql> SELECT r.reviewername,m.movietitle,ra.reviewstars
-> FROM movie m JOIN rating ra,reviewers r
-> WHERE m.movieid=ra.movieid and
-> ra.reviewerid=r.reviewerid
-> ORDER BY
-> reviewername,
-> movietitle,
-> reviewstars;
+--------------+------------+-------------+
| reviewername | movietitle | reviewstars |
+--------------+------------+-------------+
| kevin | annie hall | 4 |
| kevin | bagubali | 4 |
| nagaraj | jumanji | 2 |
| snagan | vallavan | 5 |
+--------------+------------+-------------+

Try like this
SELECT *
FROM table_name
ORDER BY column_name_1,column_name_2,column_name_3 ASC

Related

How to cross join with json and get all data

I'm tryng to use a join function in my SQL request.
I have two table :
tbl_jsontesting
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| id | data | description |
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | {"complexProperties":[{"properties":{"key":"Registred","Value":"123456789"}},{"properties":{"key":"Urgency","Value":"Total"}},{"properties":{"key":"ImpactScope","Value":"All"}}]} | Some Text |
| 2 | {"complexProperties":[{"properties":{"key":"Registred","Value":"123456788"}},{"properties":{"key":"Urgency","Value":"Total"}},{"properties":{"key":"ImpactScope","Value":"All"}}]} | Some Text2 |
| 3 | {"complexProperties":[{"properties":{"key":"Urgency","Value":"Total"}},{"properties":{"key":"ImpactScope","Value":"All"}}]} | Some Text3 |
| 4 | {} | Some Text4 |
| 5 | {"complexProperties":[]} | Some Text5 |
| 6 | | Some Text6 |
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
tbl_registred
----------------------
| id | name |
----------------------
| 123456789 | Source |
| 123456788 | Cars |
----------------------
My current query :
select jt.id, rg.id as id_registred, rg.name, jt.description
from tbl_jsontesting jt
cross join jsonb_array_elements(jt.data::jsonb -> 'complexProperties') as p(props)
join tbl_registred rg
on rg.id::text = (p.props -> 'properties' ->> 'Value')
and p.props -> 'properties' ->> 'key' = 'Registred'
;
Result :
--------------------------------------------
| id | id_registred | name | description |
--------------------------------------------
| 2 | 123456788 | Cars | Some Text2 |
| 1 | 123456789 | Source | Some Text |
--------------------------------------------
Expected result :
--------------------------------------------
| id | id_registred | name | description |
--------------------------------------------
| 6 | | | Some Text6 |
| 5 | | | Some Text5 |
| 4 | | | Some Text4 |
| 3 | | | Some Text3 |
| 2 | 123456788 | Cars | Some Text2 |
| 1 | 123456789 | Source | Some Text |
--------------------------------------------
Fiddle : https://www.db-fiddle.com/f/5Jvq4SXUpBvJsY7H3G13xm/5
in your fiddle colunm tbl_jsontesting.description is named name :)
other than that
you can do a full join in place of the second join to keep all the line ;
then add the line whitout id_registred to your first result :
WITH all_line as (
select jt.id, rg.id as id_registred, rg.name, jt.description
from tbl_jsontesting jt
cross join jsonb_array_elements(jt.data::jsonb -> 'complexProperties') as p(props)
LEFT join tbl_registred rg
on rg.id::text = (p.props -> 'properties' ->> 'Value')
and p.props -> 'properties' ->> 'key' = 'Registred'
),line_registred as (
SELECT *
FROM all_line
WHERE id_registred IS NOT NULL
)
SELECT * FROM line_registred
UNION ALL
SELECT distinct * --distinct because of line generate by sub properties in json
FROM all_line
WHERE id NOT IN (SELECT id FROM line_registred)
probably not the shortest way, but you have what you excpected :
| id | id_registred | name | description |
| --- | ------------ | ------ | ----------- |
| 1 | 123456789 | Source | Some Text |
| 2 | 123456788 | Cars | Some Text2 |
| 3 | | | Some Text3 |
next to your edit :
line 6 in is not valid ('' is not json)
line 4 and 5 doesn't have 'properties'
so
select id, jsonb_array_elements(data::jsonb -> 'complexProperties') FROM tbl_jsontesting
does'nt return anything for that 2 lines
you could do the same tricks that we done for line not 'Registred'
with matched line then all line
give you some thing like :
**Query #1**
WITH line_with_properties as (
select id,description, jsonb_array_elements(data::jsonb -> 'complexProperties') as props
FROM tbl_jsontesting
),all_json_line as (
SELECT id,description, props
FROM line_with_properties
UNION ALL
SELECT id,description, '{}' as props
FROM tbl_jsontesting
WHERE id NOT IN (SELECT id from line_with_properties)
), all_line as(
select ajl.id, rg.id as id_registred, rg.name, ajl.description
from all_json_line ajl
LEFT join tbl_registred rg
on rg.id::text = (ajl.props -> 'properties' ->> 'Value')
and ajl.props -> 'properties' ->> 'key' = 'Registred'
),line_registred as (
SELECT *
FROM all_line
WHERE id_registred IS NOT NULL
)
SELECT * FROM line_registred
UNION ALL
SELECT distinct *
FROM all_line
WHERE id NOT IN (SELECT id FROM line_registred)
order by id;
and again what you expected tested in your fiddle (without line 6):
| id | id_registred | name | description |
| --- | ------------ | ------ | ----------- |
| 1 | 123456789 | Source | Some Text |
| 2 | 123456788 | Cars | Some Text2 |
| 3 | | | Some Text3 |
| 4 | | | Some Text4 |
| 5 | | | Some Text5 |

Flatten Postgers left join query result with dynamic values into one row

I have two tables products and product_attributs. One Product can have one or many attributs and these are filled by a dynamic web form (name and value inputs) added by the user as needed. For example for a drill the user could decide to add two attributs : color=blue and power=100 watts. For another product it could be 3 or more different attribus and for another it could have no special attributs.
products
| id | name | identifier | identifier_type | active
| ----------|--------------|-------------|------------------|---
| 1 | Drill | AD44 | barcode | true
| 2 | Polisher | AP211C | barcode | true
| 3 | Jackhammer | AJ2133 | barcode | false
| 4 | Screwdriver | AS4778 | RFID | true
product_attributs
|id | name | value | product_id
|----------|--------------|-------------|----------
|1 | color | blue | 1
|2 | power | 100 watts | 1
|3 | size | 40 cm | 2
|4 | energy | electrical | 3
|4 | price | 35€ | 3
so attributs could be anything which are set dynamically by the user. My need is to generate a report on CSV which contain all products with their attributs. Without a good experience in SQL I generated the following basic request :
SELECT pr.name, pr.identifier_type, pr.identifier, pr.active, att.name, att.value
FROM products as pr
LEFT JOIN product_attributs att ON pr.id = att.product_id
as you know the result will contain for the same product as many rows as attributs it has and this is not ideal for reporting. The ideal would be this :
|name | identifier_type | identifier | active | name | value | name | value
|-----------|-----------------|------------|--------|--------|-------|------ |------
|Drill | barcode | AD44 | true | color | blue | power | 100 w
|Polisher | barcode | AP211C | true | size | 40 cm | null | null
|Jackhammer | barcode | AJ2133 | true | energy | elect | price | 35 €
|Screwdriver| barcode | AS4778 | true | null | null | null | null
here I only showed a max of two attributes per product but it could be more if needed. Well I did some research and came across the pivot with crosstab function on Postgres but the problem it requests static values but this does not match my need.
thanks lot for your help and sorry for duplicates if any.
Thanks Laurenz Albe for your help. array_agg solved my problem. Here is the query if someone may be interested in :
SELECT
pr.name, pr.description, pr.identifier_type, pr.identifier,
pr.internal_identifier, pr.active,
ARRAY_TO_STRING(ARRAY_AGG (oa.name || ' = ' || oa.value),', ') attributs
FROM
products pr
LEFT JOIN product_attributs oa ON pr.id = oa.product_id
GROUP BY
pr.name, pr.description, pr.identifier_type, pr.identifier,
pr.internal_identifier, pr.active
ORDER BY
pr.name;

how to unnest multiple arrays and transpose table in postgres 9.5

Lets suppose i have this table:
project |leader|coordination|staff |support |
---------|------|------------|-------------------|-----------|
project_a|Bob |{Alice} |{Terry,Mandy,James}|{Bob,Peter}|
project_b|Alice |{Terry, Bob}|{Mandy,James} |{Maggie} |
project_c|Maggie|{Bob} |{Terry,Peter} |{Alice,Bob}|
and i want to transform it to something like this:
person|project_1|role_1|project_2|role_2 |project_3|role_3 |project_4|role_4 |
------|---------|------|---------|------------|---------|-------|---------|------------|
Alice |project_b|leader|project_a|coordination|project_c|support| | |
Bob |project_a|leader|project_a|support |project_b|staff |project_c|coordination|
Terry...
Maggie...
...
Big thanks to #abelisto for pointing me in the right direction in the comments.
The problem with crosstab is, that i need the project also in the row:
SELECT *
FROM crosstab(
'
select
person,
project,
role
from
tmp_projects
cross join lateral (
select
\'leader\'::text as role, leader as person
union all
select
\'coordination\', x
from
unnest(coordination) as x
union all
select
\'staff\', x
from
unnest(staff) as x
union all
select
\'support\', x
from
unnest(support) as x) as t order by 1,2' -- needs to be "ORDER BY 1,2" here
, 'SELECT DISTINCT project FROM tmp_projects ORDER BY 1'
) AS ct ("Person" text, "Project_1" text,"Project_2" text,"Project_3" text);
#Abelisto had the correct idea and a solution could be:
create view tmp_view_projects_unnest as select
person,
row_number() OVER (PARTITION by person) AS rn,
project,
role
from
tmp_projects
cross join lateral (
select
'leader'::text as role, leader as person
union all
select
'coordination', x
from
unnest(coordination) as x
union all
select
'staff', x
from
unnest(staff) as x
union all
select
'support', x
from
unnest(support) as x) as t order by 1,2
person|rn|project |role |
------|--|---------|------------|
Alice | 1|project_a|leader |
Alice | 2|project_d|coordination|
Alice | 3|project_c|support |
Bob | 1|project_d|coordination|
Bob | 2|project_c|coordination|
Bob | 3|project_c|support |
Maggie| 1|project_c|leader |
Mandy | 1|project_d|leader |
Marry | 1|project_d|staff |
Peter | 1|project_d|support |
Peter | 2|project_c|staff |
Peter | 3|project_a|coordination|
Terry | 1|project_a|coordination|
Terry | 2|project_c|staff |
select tvp.person,
tvp.project as project_1, tvp.role as role_1 ,
tvp2.project as project_2, tvp2.role as role_2,
tvp3.project as project_3, tvp3.role as role_3,
tvp4.project as project_4, tvp4.role as role_4
from tmp_view_projects_unnest tvp
left join tmp_view_projects_unnest tvp2 on tvp2.person = tvp.person and tvp2.rn =2
left join tmp_view_projects_unnest tvp3 on tvp3.person = tvp.person and tvp3.rn =3
left join tmp_view_projects_unnest tvp4 on tvp4.person = tvp.person and tvp4.rn =4
where tvp.rn=1;
person|project_1|role_1 |project_2|role_2 |project_3|role_3 |project_4|role_4|
------|---------|------------|---------|------------|---------|------------|---------|------|
Alice |project_a|leader |project_d|coordination|project_c|support | | |
Bob |project_d|coordination|project_c|coordination|project_c|support | | |
Maggie|project_c|leader | | | | | | |
Mandy |project_d|leader | | | | | | |
Marry |project_d|staff | | | | | | |
Peter |project_d|support |project_c|staff |project_a|coordination| | |
Terry |project_a|coordination|project_c|staff | | | | |

PostgreSQL - Join three tables and add conditions?

I've got three tables in a PostgreSQL db that looks like this: https://imgur.com/a/bUapsYi
One user can belong to many projects, and one project can have many users, and I'm tying it together through a joined table called "userprojects".
Each table could look like this:
User
| id | firstname | lastname | email |
|----|-----------|----------|----------------|
| 1 | Joe | Green | joe#green.com |
| 2 | Olle | Svensson | olle#gmail.com |
| 3 | Erik | Yapp | erik#yapp.com |
Project
| id | name | owner |
|----|---------------|----------------|
| 1 | Project X | joe#green.com |
| 2 | Peanut Butter | olle#gmail.com |
| 3 | Apollo 11 | erik#yapp.com |
| 4 | RCPP | erik#yapp.com |
Userprojects
| id | user_id | project_id |
|----|---------|------------|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 3 |
| 4 | 3 | 3 |
Is there some form of inner(?) join that let's me query on users in a project (eg user_id found in userprojects) OR if the user is an owner of a project?
With the example above, an inner join query that looks like this:
SELECT "project".id, "project".name, "email"
FROM userprojects
INNER JOIN project ON userprojects.project_id = project.id
INNER JOIN "user" ON userprojects.user_id = "user".id
would return this:
| id | name | email |
|----|---------------|----------------|
| 1 | Project X | joe#green.com |
| 2 | Peanut Butter | joe#green.com |
| 3 | Apollo 11 | olle#gmail.com |
| 4 | Apollo 11 | erik#yapp.com |
What I wish to add to the query result is also the owner of each project if they are not found in that inner join query - notice that erik#yapp.com is the owner of project RCPP but since that relation is not found in the userprojects table, it won't be returned in the query. Can I somehow also get my query to return those users, eg:
| id | name | email |
|-----|---------------|----------------|
| 1 | Project X | joe#green.com |
| 2 | Peanut Butter | joe#green.com |
| 3 | Apollo 11 | olle#gmail.com |
| 4 | Apollo 11 | erik#yapp.com |
| (?) | RCPP | erik#yapp.com |
Start the joins with the table Project and turn them to LEFT joins:
SELECT
"project".id, "project".name,
COALESCE("User"."email", "project"."owner") "owner"
FROM project
LEFT JOIN userprojects ON userprojects.project_id = project.id
LEFT JOIN "User" ON userprojects.user_id = "User".id
See the demo.
Results:
| id | name | owner |
| --- | ------------- | -------------- |
| 1 | Project X | joe#green.com |
| 2 | Peanut Butter | joe#green.com |
| 3 | Apollo 11 | olle#gmail.com |
| 3 | Apollo 11 | erik#yapp.com |
| 4 | RCPP | erik#yapp.com |
You can create a second INNER JOIN with the table user but this
time matching the email column of the user table to the owner of the project table.
SELECT p.id, P.name, email, CONCAT(u2.firstname, " ", u.lastname) as owner
FROM userprojects up
INNER JOIN project p ON(up.project_id = p.id)
INNER JOIN user u ON(up.user_id = u.id)
INNER JOIN user u2 ON(u2.email = p.owner)

postgres sql : getting unified rows

I have one table where I dump all records from different sources (x, y, z) like below
+----+------+--------+
| id | source |
+----+--------+
| 1 | x |
| 2 | y |
| 3 | x |
| 4 | x |
| 5 | y |
| 6 | z |
| 7 | z |
| 8 | x |
| 9 | z |
| 10 | z |
+----+--------+
Then I have one mapping table where I map values between sources based on my usecase like below
+----+-----------+
| id | mapped_id |
+----+-----------+
| 1 | 2 |
| 1 | 9 |
| 3 | 7 |
| 4 | 10 |
| 5 | 1 |
+----+-----------+
I want merged results where I can see only unique results like
+-----+------------+
| id | mapped_ids |
+-----+------------+
| 1 | 2,9,5 |
| 3 | 7 |
| 4 | 10 |
| 6 | null |
| 8 | null |
+-----+------------+
I am trying different options but could not figure this out, is there way I can write joins to do this. I have to use the mapping table where associations are stored and identify unique records along with records which are not mapped anywhere.
My understanding is, you want to see all dump_table IDs that do not appear in the mapping_id column and then aggregate the mapped_ids for those that are left:
select d1.id,
array_agg(m1.mapped_id order by m1.mapped_id) filter (where m1.mapped_id is not null) as mapped_ids
from dump_table d1
left join mapping_table m1 using (id)
where not exists (select *
from mapping_table m2
where m2.mapped_id = d1.id)
group by d1.id;
Online example: https://rextester.com/JQZ17650
Try something like this:
SELECT id, name, ARRAY_AGG(mapped_id) AS mapped_ids
FROM table1 AS t1
LEFT JOIN table2 AS t2 USING (id)
GROUP BY id, name