Not able to figure out the exact PostgreSQL query - sql

I have a set of postgreSQL data which looks like the one in the attached pic(Pic1-Sample Data) :
custom_field column would identify if the row entry is 'Department ID' or 'High Priority Role?' and the first six digits of text_value column would give the department codes.
As you can see value in the text_value column is 'Yes' wherever the custom_field is 'High Priority Role?'
Now my requirement is to extract only those records where custom_field is 'High Priority Role'. This sounds very simple but it should also have the 'Department ID' attached to it.
NOTE: job_id is not unique. Each job_id corresponding to 'High Priority Role?' would also have a duplicate entry for which the value in custom_field would be 'Department ID'(see attached Pic 2 for reference).

Something like this?
select
a.*,
b.display_value as department
from (
select
*
from
<table>
where
custom_field = 'High Priority Role?'
) a
inner join
<table> b
on
a.job_id = b.job_id
where
b.custom_field != 'High Priority Role?'
group by
a.job_id,
a.custom_field,
a.float_value,
a.date_value,
a.display_value,
a.text_value,
a.unit,
b.display_value
I've tested in MariaDB, not PostgreSQL, but it should work.
http://sqlfiddle.com/#!15/62ca4/3/0

This is a simple join:
select t.*, left(t2.text_value, 6) as department_id
from t join
t t2
on t.job_id = t2.job_id and
t.custom_field = 'High Priority Role?' and
t2.custom_field <> 'High Priority Role?';
This assumes that there is exactly one matching job with the department id.
If there are multiple, then go for a lateral join:
select t.*, left(t2.text_value, 6) as department_id
from t, lateral
(select t2.*
from t t2
where t.job_id = t2.job_id and
t.custom_field = 'High Priority Role?' and
t2.custom_field <> 'High Priority Role?'
fetch first 1 row only -- no `order by` because any match will do
) t2;

Related

How to combine multiple records in one in SQL

I have a SQL table that looks something like this:
OP
ID
First name
Last name
Phone number
I
123
John
Smith
888-555
U
123
777-555
I have to combine this rows through select query into something like this:
ID
First name
Last name
Phone number
123
John
Smith
777-555
I have trouble writing query because my only idea is with MAX, but sometimes is field from U row lesser than the one in I row and I need to check every column.
What I tried:
Select ID,
max('First name') as 'First name',
max('Last name') as 'Last name',
max('Phone number') as 'Phone number'
from table
group by ID
Please help.
You seem to want a priority, with U first then I. One method is to use coalesce() with conditional aggregation:
select id,
coalesce(max(case when OP = 'I' then first_name end),
max(first_name)
) as first_name,
coalesce(max(case when OP = 'I' then last_name end),
max(last_name)
) as last_name,
coalesce(max(case when OP = 'I' then phone_number end),
max(phone_number)
) as phone_number
from t
group by id;
You could use the JOIN keyword to join the two tables together with the max values included.
Here's a link to reference the code you're looking for: Select only rows by join tables max value
You can use an autoreferenced JOIN to search the not null rows and OP = I, to not depends of a MAX
SELECT a.OP, a.ID,
COALESCE(a.'First name',b.'First name'),
COALESCE(a.'Last name',b.'Last name'),
'Phone number'
FROM table a
LEFT JOIN (SELECT * FROM table WHERE OP = 'I' AND and 'First name' IS NOT NULL AND 'Last name' IS NOT NULL LIMIT 1) b ON b.ID = a.ID;

Zero records for different when conditions

How do i display zero record in sqlserver if i dont find anything
select count(*) as[count],
case when status=0 then 'pending'
when status=1 then 'running'
when status=2 then 'completed'
end as status
from Queue where ID='APP'
group by status
what i want is if there is no record for status=0, how should i display 0 in the result also for other status as well. I need something like pending=0,running=0 and completed=0 if no rows found for respective status.
I tried something like below, but that does not suit my need. Any Idea?
if exists (myselect query)
select 1
else
select 0
Use values() and a left join:
select s.descr, count(q.id) cnt
from (values (0, 'pending'), (1, 'running'), (2, 'completed')) s(status, descr)
left join queue q on q.status = s.status and q.id = 'APP'
group by s.descr
This generates one row per status, with the count of rows in such status in table queue.

Comparing values on previous row to perform calculations on the current row, SQL

sql fiddle: http://sqlfiddle.com/#!4/a717b/1
Here is my Table:
Here is my Code:
select key, status, dat, sysdat,
(case when key = 1 and type = 'Car' status = 'F' then round( sysdat-dat,2) else 0 end ) as Days,
(case when key = 1 and type ='Bus' and Status = 'F' then round( sysdat-dat,2) else 0 end) as Days
from ABC
Expected Output:
So I want to calculate days between the 'dat' column and current date for the following conditions.
1) For every key, The sequence is always car first, bus second.
which means that for every key, only when the status of the car is true we check for the bus.
2) If the Status OF 'CAR' IS 'T' then I don't want to calculate the days
3) If the Status of 'Car' IS 'F' then I want to calculate the days only for the 'Car' and not for 'Bus' because it is always 'car' first 'bus' second
4) If the Status of 'Bus' is 'F' and Status of 'Car' is 'T' then I calculate the days because it matches the condition, 'Car' first and 'Bus' second.
With 2 vehicles
If you always have a car and a bus, and only a car and a bus of the same key, you could self-join the table, and check if either the vehicle a (which you're querying) is Car with status F, or if the related verhicle, b, is a car with status T. In either case you're gonna get a date, and in any other case you don't. That covers your example, and also implies that if car and bus would both be T, the date would still be shown next to the bus only.
select a.key, a.type, a.status, a.dat,
case when
(a.type = 'car' and a.Status = 'F') or -- a is a car and is F
(b.type = 'car' and b.Status = 'T') -- a is related to a car (b), which is T
then
trunc(sysdate) - a.dat
end as DAYS
from
ABC a
join ABC b on b.key = a.key and b.type <> a.type
order by
-- Sort the query by key first, then type.
a.key,
decode(a.type, 'car', 1, 2)
The query above: http://sqlfiddle.com/#!4/a717b/5/0
With N vehicles
If you have more vehicles, a different approach can be better, especially when the number of vehicles is high, or not fixed.
The query below has a list of all the vehicles and their sort order. This is an inline view now, but you could use a separate lookup table for that. A lookup table is even more flexible, because you can just add vehicle types or change their sort order.
Anyway, that lookup table/view can be joined on your main table to have a sort order for each of your record.
You can then make a ranking using window function like rank or dense_rank to introduce a numbering based on that sort order ("falsenumber"), and the fact that status is 'F'. After that, it's easy to put the date on the first row that is F (falsenumber = 1).
with
VW_TYPES as
-- This could be a lookup table as well, instead of this hard-codeslist of unions.
( select 'car' as type, 1 as sortorder from dual union all
select 'bus' as type, 2 as sortorder from dual union all
select 'train' as type, 3 as sortorder from dual union all
select 'airplane' as type, 4 as sortorder from dual union all
select 'rocket' as type, 5 as sortorder from dual),
VW_TYPESTATUS as
( select
a.*,
t.sortorder,
dense_rank() over (partition by key order by case when a.status = 'F' then t.sortorder end) as falsenumber
from
ABC a
join VW_TYPES t on t.type = a.type)
select
ts.key, ts.type, ts.status, ts.dat,
case when ts.falsenumber= 1 then
trunc(sysdate) - ts.dat
end as DAYS
from
VW_TYPESTATUS ts
order by
ts.key, ts.sortorder
The query above: http://sqlfiddle.com/#!4/71f52/8/0
Vehicle types in a separate table: http://sqlfiddle.com/#!4/f055d/1/0
Do note that oracle is case sensitive. 'car' and 'Car' and 'CAR' are not the same thing. Use lower(type) = 'car' if you want to allow type to contain the vehicle type with any casing. Do note that that's bad for using indexes, although I think the impact isn't that bad, since you only got a couple of rows per key.
Alternatively (arguably better), you could introduce a numeric VehicleTypeId in the new types table, and use that id in the ABC table, instead of the string 'Car'.

SQL QUERY--Need help in finding a particular percentage

There are two tables with the below columns:
table1: asin_details(asin,contact_id) primary key: contact_id
table2: contact_details(contact_date,contact_id,hmd_response(y or n)) primary key: contact_id
I've to find specific percentage from the above data using a formula called no. of n's/no. of y's *100 which is called as "FRR"
So, the actual question is to find the "FRR" of all the asin's with which customers contacted us between 01-jan-2013 and 31-dec-2013
I'm actually struggling with the part where you need to calculate the "FRR" from the date provided above. Please help.
Thanks,
SELECT (( select count(hmd_response) from contact_details where hmd_response= 'n')/( select count(hmd_response) from contact_details where hmd_response= 'y') )*100 as FRR
from asin_details A
join contact_details C
on c.contact_id = A.contact_id
where contact_date between '01-jan-2013' and '31-dec-2013'
OR
SELECT ( ( select count(hmd_response) from contact_details where hmd_response= 'n'
AND between '01-jan-2013' and '31-dec-2013')/
( select count(hmd_response) from contact_details where hmd_response= 'y'
AND between '01-jan-2013' and '31-dec-2013') )*100 as FRR
Try this out and let me know.
I am basically calculating the counts of y's and n's and then i am calculating the FRR.
Should be something like this:
SELECT
a.asin,
((count(case c.hmd_response when 'n' then 1 end) /
count(case c.hmd_response when 'y' then 1 end)) * 100) as FRR,
a.contact_id
from asin_details a
join contact_details c
on a.contact_id = c.contact_id
where c.contact_date between '01-jan-2013' and '31-dec-2013'

ORACLE SQL Returning most recently added record detail

Edit: Solved with a lot of help from Dems, I will post a truncated version of the query which is now working very well!
SELECT
*
FROM
O_PERSONS
/*The below left join returns the most recently added ethnicity classification*/
LEFT JOIN
(SELECT
ROW_NUMBER() OVER (PARTITION BY O_CLASSIFICATIONS.CLA_SUBJECT_ID ORDER BY O_CLASSIFICATIONS.CLA_DATE_NOTIFIED DESC) AS Sequence_ID,
O_CLASSIFICATIONS.CLA_CAT_ID,
O_CLASSIFICATIONS.CLA_SUBJECT_ID
FROM
O_CLASSIFICATIONS
WHERE
O_CLASSIFICATIONS.CLA_SUBJECT_IND = 'P'
AND
O_CLASSIFICATIONS.CLA_TOP_CAT_ID = 'ETHNIC'
AND
O_CLASSIFICATIONS.CLA_CAT_ID <> 'DECLINED'
) ETHNIC
ON ETHNIC.CLA_SUBJECT_ID = O_PERSONS.PER_ID
AND ETHNIC.Sequence_ID = 1
/*The below left join returns the most recently added PCG classification*/
LEFT JOIN
(SELECT
ROW_NUMBER() OVER (PARTITION BY O_CLASSIFICATIONS.CLA_SUBJECT_ID ORDER BY O_CLASSIFICATIONS.CLA_DATE_NOTIFIED DESC) AS Sequence_ID,
O_CLASSIFICATIONS.CLA_CAT_ID,
O_CLASSIFICATIONS.CLA_SUBJECT_ID
FROM
O_CLASSIFICATIONS
WHERE
O_CLASSIFICATIONS.CLA_SUBJECT_IND = 'P'
AND
O_CLASSIFICATIONS.CLA_TOP_CAT_ID = 'PRIMARY'
AND
O_CLASSIFICATIONS.CLA_CAT_ID <> 'DECLINED'
) PCG
ON PCG.CLA_SUBJECT_ID = O_PERSONS.PER_ID
AND PCG.Sequence_ID = 1
WHERE
O_PERSONS.PER_ID LIKE 'P%'
I am currently trying to write a query which will return certain details for clients on our system. Whilst a client will only have one date of birth, gender, and P ID, they may have multiple ethnicities (as a result of data quality) and also client groups (this is more legitimate as a client's needs may change). However, for these particular items, I am only interested in the most recently added ethnicities or client groups. Below I have written a query which assigns a row number to the ethnicity and to the client group with a plan to return only those which equal 1, as these generally will be the most recent. However, I have run into 2 issues, one of which I can resolve, the other of which I'm not so sure.
First off, some clients will not have an ethnicity or a client group recorded. When running the below query, they return with very odd row numbers (often in the thousands). However, I know in my 'where' that I could specify that a row is returned if client group or ethnicity was empty (as these will be data quality cases which need to be addressed).
The second issue is slightly trickier which I will try to represent with a table below. Please note I have abbreviated the table for ease of input and understanding.
P ID Ethnicity PCG RN E RN P
P1 WB OV 1 2
P1 WI OV 2 1
The above client had two ethnicities, though WB is the most recent (therefore the 1 in Row Number E is correct). However, the client has only had one PCG recorded but the row number returns a 2 in the first row (arguably the row I want to return). I'm not sure why though I guess because ETHNIC.CLA_SUBJECT_ID is joined on O_PERSONS.PER_ID and PER_ID occurs twice, that is why it thinks there is two rows for that particular field. However, even if that is the case, is there a way to force just 1's to appear in one row? Or could I do this a completely different way? Hopefully this query makes some sense, apologies if parts are unclear. Thanks,
SELECT
O_PERSONS.PER_ID as "P ID",
olm_bo.get_per_name(O_PERSONS.PER_ID) as "Full Name",
O_PERSONS.PER_BIRTH_DATE as "Date of Birth",
case
when O_PERSONS.PER_DECEASED_DATE is null then FLOOR(MONTHS_BETWEEN(sysdate,O_PERSONS.PER_BIRTH_DATE)/12)
else FLOOR(MONTHS_BETWEEN(O_PERSONS.PER_DECEASED_DATE,O_PERSONS.PER_BIRTH_DATE)/12)
end as "Age",
O_PERSONS.PER_DECEASED_DATE as "Date Deceased",
olm_bo.get_gender_desc('P', O_PERSONS.PER_GENDER) as "Gender",
CASE
WHEN ETHNIC.CLA_CAT_ID IN ('C1','C2','C3','C4','ABAN','AIND','AOTH','APKN') THEN 'Asian or Asian British'
ELSE NULL
End as "Ethnicity - Top" ,
CASE
WHEN ETHNIC.CLA_CAT_ID IN ('BAFR','D2') THEN 'African'
ELSE NULL
End as "Ethnicity - Detail" ,
CASE
WHEN PCG.CLA_CAT_ID IN ('ASYLUM','REFUGEE') THEN 'Asylum Seeker/Refugee'
ELSE NULL
End as "PCG",
CASE
WHEN PCG.CLA_CAT_ID IN ('ASYLUM','REFUGEE') THEN 'Asylum Seeker/Refugee'
ELSE NULL
End as "PCG - Top",
CASE
WHEN PCG.CLA_CAT_ID IN ('ASYLUM','REFUGEE') THEN 'Asylum Seeker/Refugee'
ELSE NULL
End as "PCG - DETAIL",
to_char(row_number() over(PARTITION BY ETHNIC.CLA_SUBJECT_ID ORDER BY abs(sysdate - ETHNIC.CLA_DATE_NOTIFIED) asc)) as "Row Number E",
to_char(row_number() over(PARTITION BY PCG.CLA_SUBJECT_ID ORDER BY abs(sysdate - PCG.CLA_DATE_NOTIFIED) asc))as "Row Number P"
FROM
O_PERSONS
LEFT JOIN O_CLASSIFICATIONS ETHNIC ON ETHNIC.CLA_SUBJECT_ID = O_PERSONS.PER_ID
AND ETHNIC.CLA_SUBJECT_IND = 'P'
AND ETHNIC.CLA_TOP_CAT_ID = 'ETHNIC'
AND ETHNIC.CLA_CAT_ID <> 'DECLINED'
LEFT JOIN O_CLASSIFICATIONS PCG ON PCG.CLA_SUBJECT_ID = O_PERSONS.PER_ID
AND PCG.CLA_SUBJECT_IND = 'P'
AND PCG.CLA_TOP_CAT_ID = 'PRIMARY'
AND PCG.CLA_CAT_ID <> 'DECLINED'
WHERE
/*Following line excludes any clients whose is less than 18)*/
O_PERSONS.PER_BIRTH_DATE > trunc(add_months(O_PERSONS.PER_BIRTH_DATE,-216))
AND O_PERSONS.PER_ID LIKE 'P%'
You should apply ROW_NUMBER() before you do the joins.
SELECT
* -- Your calculations here, '*' used for brevity
FROM
O_PERSONS
LEFT JOIN
(
SELECT
ROW_NUMBER() OVER (PARTITION BY CLA_SUBJECT_ID ORDER BY CLA_DATE_NOTIFIED DESC) AS sequence_id,
*
FROM
O_CLASSIFICATIONS
WHERE
CLA_SUBJECT_IND = 'P'
AND CLA_TOP_CAT_ID = 'ETHNIC'
AND CLA_CAT_ID <> 'DECLINED'
)
ETHNIC
ON ETHNIC.CLA_SUBJECT_ID = O_PERSONS.PER_ID
AND ETHNIC.sequence_id = 1
LEFT JOIN
(
SELECT
ROW_NUMBER() OVER (PARTITION BY CLA_SUBJECT_ID ORDER BY CLA_DATE_NOTIFIED DESC) AS sequence_id,
*
FROM
O_CLASSIFICATIONS
WHERE
CLA_SUBJECT_IND = 'P'
AND CLA_TOP_CAT_ID = 'PRIMARY'
AND CLA_CAT_ID <> 'DECLINED'
)
PCG
ON PCG.CLA_SUBJECT_ID = O_PERSONS.PER_ID
AND PCG.sequence_id = 1
WHERE
/*Following line excludes any clients whose is less than 18)*/
O_PERSONS.PER_BIRTH_DATE > trunc(add_months(O_PERSONS.PER_BIRTH_DATE,-216))
AND O_PERSONS.PER_ID LIKE 'P%'