Split postgresql table column into multiple columns - sql

Ok so this is the partial query, working 100% as we speak. There's a comment as you can see where the query of the 4 columns is supposed to go.
SELECT DISTINCT
p.id AS "Project ID",
p.title AS "Project Title",
p.summary AS "Project Summary",
to_char(p.expected_start_date, 'YYYY-MM-dd') AS "Expected Start Date",
to_char(p.expected_end_date, 'YYYY-MM-dd') AS "Expected End Date",
to_char(p.actual_start_date, 'YYYY-MM-dd') AS "Actual Start Date",
to_char(p.actual_end_date, 'YYYY-MM-dd') AS "Actual End Date",
d.name AS "Center/Department",
/* THIS IS WHERE THE Query has to go.*/
SELECT DISTINCT string_agg(distinct gd.name ||' ('||gs.name||')', ' | ') AS "GRANT NAME AND STATUS"
from grant_detail gd
JOIN grant_status gs on gd.status_id = gs.id
JOIN project_budget pb ON gd.id = pb.grant_id
WHERE pb.project_id = p.id
group by p.id),
SELECT DISTINCT string_agg(distinct o.name, ', ') AS "FUNDER"
FROM organization o
JOIN organization_type ot ON ot.id = o.type_id
JOIN grant_detail gt ON gt.organization_id = o.id
JOIN project_budget pb ON pb.grant_id = gt.id
WHERE pb.project_id = p.id
AND ot.name = 'Funder'),
SELECT string_agg(pc.name, ', ') AS "Categories"
FROM project_project_categories ppc
JOIN project_category pc ON ppc.project_category_id = pc.id
WHERE ppc.project_id = p.id),
( /*========= Student Researcher Full Name (+email) =========*/
SELECT DISTINCT string_agg(distinct s.first_name || ' ' ||
s.last_name ||' ('||
s.email ||')', ', ')
AS "Student Researcher"
FROM project_stakeholder ps
JOIN stakeholder s ON s.id = ps.stakeholder_id
JOIN project_role pr ON ps.role_id = pr.id
WHERE pr.name = 'Student Researcher'
AND ps.project_id = p.id
GROUP BY p.id)
FROM
/*CONTEXT*/
project p
LEFT JOIN innovation_category c2 ON p.innovation_category_id = c2.id
LEFT JOIN department d ON p.department_id = d.id
JOIN project_health ph ON p.health_id = ph.id
JOIN project_status ps ON p.status_id = ps.id
-- WHERE
-- -- p.actual_start_date <= '__beforeActualStartDate'
-- -- AND p.actual_end_date >= '__afterActualEndDate'
-- -- AND p.expected_start_date <= '__beforeStartDate'
-- -- AND p.expected_end_date >= '__afterEndDate'
ORDER BY
p.title
UPDATE: For Phelipe:
You can see where I've commented my code in the place where the four columns need to be que

Here is a fully dynamic solution that can work with a variable list of roles.
This solution relies on the creation of a composite type role_type which includes the list of roles and which can be called at the runtime :
CREATE OR REPLACE PROCEDURE role_type () LANGUAGE plpgsql AS
$$
DECLARE
role_list text ;
BEGIN
SELECT string_agg(r_name || ' text', ',' ORDER BY r_id)
INTO role_list
FROM role ;
EXECUTE 'DROP TYPE IF EXISTS role_type' ;
EXECUTE 'CREATE TYPE role_type AS (' || role_list || ')' ;
END ;
$$
After calling the procedure role_type(), the result is provided by a simple query using the jsonb_object_agg and jsonb_populate_record standard functions :
CALL role_type () ;
SELECT a.p_id AS "Project id"
, a.p_title AS "Project Title"
, (jsonb_populate_record(NULL :: role_type, jsonb_object_agg(lower(a.r_name), (a.usr_list)))).*
FROM
( SELECT p.p_id
, p.p_title
, r.r_name
, string_agg(u.fname || ' ' || u.lname, ', ') AS usr_list
FROM project_usr AS pu
INNER JOIN project AS p
ON p.p_id = pu.project_id
INNER JOIN usr AS u
ON u.usr_id = pu.usr_id
INNER JOIN role AS r
ON r.r_id = pu.role_id
GROUP BY p.p_id, p.p_title, r.r_name
) AS a
GROUP BY a.p_id, a.p_title
ORDER BY a.p_id
Finally, instead of calling the role_type() procedure each time before executing the query, it can be called by trigger when the role list is changing :
CREATE OR REPLACE FUNCTION role_after_insert_update ()
RETURNS trigger LANGUAGE plpgsql AS
$$
BEGIN
CALL role_type () ;
RETURN NEW ;
END ;
$$ ;
CREATE OR REPLACE TRIGGER role_after_insert_update AFTER INSERT OR UPDATE ON role
FOR EACH ROW EXECUTE FUNCTION role_after_insert_update() ;
CREATE OR REPLACE FUNCTION role_after_delete ()
RETURNS trigger LANGUAGE plpgsql AS
$$
BEGIN
CALL role_type () ;
RETURN OLD ;
END ;
$$ ;
CREATE OR REPLACE TRIGGER role_after_delete AFTER DELETE ON role
FOR EACH ROW EXECUTE FUNCTION role_after_delete() ;
see the full test result in dbfiddle

you can do this but it supposes that role table is always the same with same id
select pu.project_id,
case r.r_id when 1 then u.fname||' '||u.lname else '' end as Teacher,
case r.r_id when 2 then u.fname||' '||u.lname else '' end as Student,
case r.r_id when 3 then u.fname||' '||u.lname else '' end as Volunteer
from project_usr pu join role r on r.r_id = pu.role_id
join usr u on pu.usr_id = u.usr_id
Result here
Crosstab
You could use crosstab too but the problem will be the same, you have to know the roles
select * from crosstab($$
select pu.project_id,r.r_name, u.fname||' '||u.lname
from project_usr pu join role r on r.r_id = pu.role_id
join usr u on pu.usr_id = u.usr_id
$$,'select r_name from role')
as final_result (project_id integer, Teacher varchar,Student varchar,Volunteer varchar)
Result here
Maybe something like this (not tested, not enough informations)
with r as (
select * from crosstab($$
select pu.project_id,r.r_name, u.fname||' '||u.lname
from project_usr pu join role r on r.r_id = pu.role_id
join usr u on pu.usr_id = u.usr_id
$$,'select r_name from role')
as final_result (project_id integer, Teacher varchar,Student varchar,Volunteer varchar)
)
SELECT DISTINCT
p.id AS "Project ID",
p.title AS "Project Title",
p.summary AS "Project Summary",
to_char(p.expected_start_date, 'YYYY-MM-dd') AS "Expected Start Date",
to_char(p.expected_end_date, 'YYYY-MM-dd') AS "Expected End Date",
to_char(p.actual_start_date, 'YYYY-MM-dd') AS "Actual Start Date",
to_char(p.actual_end_date, 'YYYY-MM-dd') AS "Actual End Date",
d.name AS "Center/Department",
/* THIS IS WHERE THE Query has to go.*/
r.teacher,
r.student,
r.volunteer,
SELECT DISTINCT string_agg(distinct gd.name ||' ('||gs.name||')', ' | ') AS "GRANT NAME AND STATUS"
from grant_detail gd
JOIN grant_status gs on gd.status_id = gs.id
JOIN project_budget pb ON gd.id = pb.grant_id
WHERE pb.project_id = p.id
group by p.id),
SELECT DISTINCT string_agg(distinct o.name, ', ') AS "FUNDER"
FROM organization o
JOIN organization_type ot ON ot.id = o.type_id
JOIN grant_detail gt ON gt.organization_id = o.id
JOIN project_budget pb ON pb.grant_id = gt.id
WHERE pb.project_id = p.id
AND ot.name = 'Funder'),
SELECT string_agg(pc.name, ', ') AS "Categories"
FROM project_project_categories ppc
JOIN project_category pc ON ppc.project_category_id = pc.id
WHERE ppc.project_id = p.id),
( /*========= Student Researcher Full Name (+email) =========*/
SELECT DISTINCT string_agg(distinct s.first_name || ' ' ||
s.last_name ||' ('||
s.email ||')', ', ')
AS "Student Researcher"
FROM project_stakeholder ps
JOIN stakeholder s ON s.id = ps.stakeholder_id
JOIN project_role pr ON ps.role_id = pr.id
WHERE pr.name = 'Student Researcher'
AND ps.project_id = p.id
GROUP BY p.id)
FROM
/*CONTEXT*/
project p
LEFT JOIN innovation_category c2 ON p.innovation_category_id = c2.id
LEFT JOIN department d ON p.department_id = d.id
JOIN project_health ph ON p.health_id = ph.id
JOIN project_status ps ON p.status_id = ps.id
/* don't know the key */
join r on ...
ORDER BY
p.title

Related

How to unite several tables in a one so the names of the columns became the row names?

for instance I have
SELECT customer_id, first_name || ', ' || last_name || ', ' || email as "customer's info"
FROM customer
WHERE customer_id = 5
;
SELECT count(i.film_id) AS "num.of films rented" FROM payment p
JOIN rental r ON p.rental_id = r.rental_id
JOIN inventory i ON r.inventory_id = i.inventory_id
WHERE r.rental_date >= ('2014-01-01'::date)
AND r.rental_date <= ('2017-05-03'::date)
AND p.customer_id = 5
;
I want in output
metric1 | metric2
----------------------------
customer's info | blalalalal
num.of films rented | blalalalal
I try smth like, but nothing
SELECT * FROM crosstab(
SELECT first_name || ', ' || last_name || ', ' || email
FROM customer WHERE customer_id = 5,
SELECT count(i.film_id) FROM payment p
JOIN rental r ON p.rental_id = r.rental_id
JOIN inventory i ON r.inventory_id = i.inventory_id
WHERE r.rental_date >= ('2014-01-01'::date)
AND r.rental_date <= ('2017-05-03'::date))
AS ('fjfjf' TEXT, 'fjfjf' int );
Could you help me?
I dont know how to do it in postgress
Thanks a lot
I would UNION ALL the two queries together - but remember to CAST the count value as a string, as you need matching data types to UNION:
SELECT
'customer''s info' AS "name"
, first_name || ', ' || last_name || ', ' || email AS "value"
FROM customer c
UNION ALL
'num.of films rented' AS "name"
, COUNT(i.film_id)::VARCHAR(5) AS "value"
FROM payment p
JOIN rental r ON p.rental_id = r.rental_id
JOIN inventory i ON r.inventory_id = i.inventory_id
WHERE r.rental_date >= ('2014-01-01'::date)
AND r.rental_date <= ('2017-05-03'::date)
WHERE customer_id = 5
;
It is unclear to me why inventory is in the second join.
SELECT 'customer''s info' as metric1,
first_name || ', ' || last_name || ', ' || email as metric2
FROM customer
WHERE customer_id = 5
UNION ALL
SELECT 'num.of films rented' as metric1, count(i.film_id)::text AS metric2
FROM payment p JOIN
rental r
ON p.rental_id = r.rental_id
WHERE r.rental_date >= '2014-01-01'::date AND
r.rental_date <= '2017-05-03'::date AND
p.customer_id = 5;
You could also combine this into a single query if you are just trying to get the results in a single result set:
SELECT (first_name || ', ' || last_name || ', ' || email) as customer_info,
count(i.film_id) as num_films
FROM payment p JOIN
rental r
ON p.rental_id = r.rental_id JOIN
customer c
ON c.customer_id = p.customer_id
WHERE r.rental_date >= '2014-01-01'::date AND
r.rental_date <= '2017-05-03'::date AND
c.customer_id = 5
GROUP BY c.customer_id;
(This puts the values in one row with two columns.) Using a subquery, the results can be easily unpivoted.

List of users in a JIRA Project

Can someone please help me with a SQL to retrieve users associated to a JIRA project?
SELECT A.ROLETYPEPARAMETER AS USERNAME, R.NAME AS ROLENAME, P.PKEY || ' - ' || P.PNAME AS PROJECTNAME
FROM PROJECTROLEACTOR A
INNER JOIN PROJECTROLE R ON A.PROJECTROLEID = R.ID
INNER JOIN PROJECT P ON A.PID = P.ID
ORDER BY 3, 1, 2;
or to just aggregate the users per project:
SELECT DISTINCT P.PKEY, LISTAGG(A.ROLETYPEPARAMETER, ',') WITHIN GROUP(ORDER BY A.ROLETYPEPARAMETER ASC) OVER(PARTITION BY P.PKEY) AS USERNAMES
FROM PROJECTROLEACTOR A
INNER JOIN PROJECTROLE R ON A.PROJECTROLEID = R.ID
INNER JOIN PROJECT P ON A.PID = P.ID
GROUP BY P.PKEY, A.ROLETYPEPARAMETER;;
For Jira 6, the correct MySQL syntax is:
SELECT
A.ROLETYPEPARAMETER AS USERNAME,
R.NAME AS ROLENAME, CONCAT(P.pkey, ' - ', P.pname) AS PROJECTNAME
FROM projectroleactor A
INNER JOIN projectrole R ON A.PROJECTROLEID = R.ID
INNER JOIN project P ON A.PID = P.ID
ORDER BY 3, 1, 2
NOTE: field names are case-sensitive

select user doesn't contain a record in other table sql query

I had three tables: sarcuser, sarcusercommittee, sarcallcourse. I need to build a query that brings all the users that don't have a committee (they don't have a record in sarcusercommittee). Here is my current query:
SELECT u.firstname + ' ' + u.lastname AS name, u.dateofbirth, u.gender, LEFT(u.note, 200) AS note, c.name AS coursename
FROM sarcuser AS u INNER JOIN
sarcusercommittee AS uc ON u.id = uc.user_id INNER JOIN
sarcallcourse AS c ON c.id = u.courseid
WHERE ((SELECT COUNT(id) AS Expr1
FROM sarcusercommittee
WHERE (user_id = u.id)) = 0)
ORDER BY name DESC
I guess the problem is in (ON condistion) but don't get it ... any help ?
NOTE : I use visual studio 2010
SELECT u.firstname + ' ' + u.lastname AS name, u.dateofbirth, u.gender, LEFT(u.note, 200) AS note, c.name AS coursename
FROM sarcuser AS u
INNER JOIN sarcallcourse AS c
ON c.id = u.courseid
WHERE u.id NOT IN (
SELECT uc.user_id
FROM sarcusercommittee AS uc
)
ORDER BY name DESC
Firstly you shouldn't inner join onto sarcusercommittee if you dont want rows from it. Seconly I would filter to the users that are not in sarcusercommittee using NOT IN.

SQL Server 2012 Query - Foreign Key from Same Table

I have a table named RoleDetails with this structure:
ROLE_ID - PK ,
NAME ,
PARENT_ROLE_ID FK ,
CREATED_BY_ID ,
MODIFIED_BY_ID
The other table is user_details
USER_ID ,
USERNAME ,
FULLNAME
How do I query this table so that in the result set
I get the RoleName, RoleId, ParentRoleId, ParentRoleName, CreatedByName and ModifiedByName.
So far I have tried:
SELECT
[ROLE_ID] AS ID, r.NAME AS [RoleName],
r.PARENT_ROLE_ID AS [ParentRoleID] ,
(SELECT rd.NAME FROM dbo.ROLES rd
WHERE rd.ROLE_ID = r.PARENT_ROLE_ID ) AS [ParentRoleName],
CONCAT(ud.FIRST_NAME, ' ', ud.LAST_name) AS [CreatedByName] ,
CONCAT(u.FIRST_NAME, ' ', u.LAST_name) AS [LastModifiedByName]
FROM
dbo.ROLES r
LEFT OUTER JOIN
user_details u ON r.MODIFIED_BY = u.USER_ID
LEFT OUTER JOIN
dbo.USER_DETAILS ud ON r.CREATED_BY = ud.USER_ID
WHERE
r.ROLE_ID = #iRoleID;
You will need to join the user_detail table twice. Once to get the create user and once to get the modify user.
SELECT rd.Name,
rd.Role_ID,
rd.Parent_role_id,
pr.Name,
ud.userName as CreatedByName,
udm.userName as ModifyByName
FROM RoleDetails as rd
INNER JOIN RoleDetails as pr
ON rd.Parent_Role_Id = pr.Role_id
INNER JOIN User_Detail as ud
on rd.Created_by_id = ud.user_id
INNER JOIN User_Detail as udm
ON rd.Modified_by_user = udm.user_id

Oracle find shareholders sub query

Trying to find all shareholders who hold more than 0.5% of the shares in any single company
here is my query I believe i am close also the image attached has the schema
![Select Sh.Share_Holder_Id Shareholderid,
Sh.First_Name||' '||Sh.Last_Name Shareholders,
c.name,
sum(T.Share_Amount) ShareAmount
From Trades T
Inner Join
Share_Holders Sh
On
T.Share_Holder_Id =Sh.Share_Holder_Id
Inner Join
Shares S
On
S.Share_Id =T.Share_Id
Inner Join
Companies C
on
C.Company_Id=S.Company_Id
Where exists (
Select
sum(case when shs.amount IS NULL THEN 1 ELSE 0 END)/count (*)*100
From Shares S1
Inner Join Share_Holder_Shares shs
On
Shs.Share_Id=S1.Share_Id
-- where Shs.Amount/t.share_amount * 100 > 0.5
)
Group By Sh.Share_Holder_Id,Sh.First_Name||' '||Sh.Last_Name,C.Name
order by Shareholderid;
Give this solution a try; it uses RATIO_TO_REPORT.
SELECT y.share_holder_id
, y.shareholders
, y.portion_of_co
, y.company_name
FROM (SELECT x.share_holder_id
, x.shareholders
, RATIO_TO_REPORT(x.share_amount)
OVER (PARTITION BY x.company_id) portion_of_co
, c.name company_name
FROM (SELECT sh.share_holder_id
, sh.first_name
|| ' '
|| sh.last_name shareholders
, s.company_id
, SUM(t.share_amount) share_amount
FROM trades t
INNER JOIN share_holders sh
ON t.share_holder_id = sh.share_holder_id
INNER JOIN shares s
ON s.share_id = t.share_id
GROUP BY sh.share_holder_id
, sh.first_name
|| ' '
|| sh.last_name
, s.company_id
) x
INNER JOIN companies c
ON x.company_id = c.company_id
) y
WHERE y.portion_of_co > 0.005
ORDER BY y.share_holder_id
;