case when subquery multiple row - db2

I have tree table on DB2 9.7.01
Table A (here are start data)
codeA |type
-----------------------------
P003 |P
K001 |K
Table B (here are product data)
codeB |description
-----------------------------
P001 |Product one
P002 |Product two
P003 |Product three
Table C (here are kit data; kit include one or more product)
codeC |description |codeB
-----------------------------
K001 |Kit one |P001
K001 |Kit one |P002
i need to read every record of A table and if field type is "K" then query must return its multiple product; if type is "P" then query return only one product;
this is the query to accomplish
Select a.codeA AS codeExternal,
case
when a.type = 'K' then (select C.CODEB from C where A.CODEA = C.CODEC)
else a.CODEA
end as codeInternal
from
A
left B
ON a.CODEA = B.CODEB
but the query return only
codeExt | code Int
---------------------------
P003 | P003
K001 | P001
and not the awaited
codeExt | code Int
---------------------------
P003 | P003
K001 | P001
K001 | P002

try Something like this:
select A.CodeA CodeExt, B.CodeB CodeInt
from A inner join B on A.CodeA=B.CodeB and A.type='P'
union all
select A.CodeA CodeExt, C.CodeB CodeInt
from A inner join C on A.CodeA=C.CodeC and A.type='K'
if you want remove doublon, use union and not union all

Related

How to join two tables with nested field?

I have a table like this:
id | ciaps
1 | a|b|c
An have a second table like:
cod | desc
a | item a
b | item b
c | item c
I need a code to join this tables like:
id | ciaps
1 | item a|item b|item c
Use array_agg for concatenating string separated by '|' and convert it array_to_string to get the value expected format.
-- PostgreSQL (v11)
SELECT t1.id, t2.descr ciaps
FROM test1 t1
INNER JOIN (SELECT array_to_string(array_agg(cod), '|') cod
, array_to_string(array_agg(descr), '|') descr
FROM test2) t2
ON t1.ciaps = t2.cod;
Please check from url https://dbfiddle.uk/?rdbms=postgres_11&fiddle=6fffc7f1da6a02a48018b3691c99ad17

Unpack expression results from case statement

Four categories in category table.
id | name
--------------
1 | 'wine'
2 | 'chocolate'
3 | 'autos'
4 | 'real estate'
Two of the many (thousands of) forecasters in forecaster table.
id | name
--------------
1 | 'sothebys'
2 | 'cramer'
Relevant forecasts by the forecasters for the categories in the forecast table.
| id | forecaster_id | category_id | forecast |
|----+---------------+-------------+--------------------------------------------------------------|
| 1 | 1 | 1 | 'bad weather, prices rise short-term' |
| 2 | 1 | 2 | 'cocoa bean surplus, prices drop' |
| 3 | 1 | 3 | 'we dont deal with autos - no idea' |
| 4 | 2 | 2 | 'sell, sell, sell' |
| 5 | 2 | 3 | 'demand for cocoa will skyrocket - prices up - buy, buy buy' |
I want prioritized mapping of (forecaster, category, forecast) such that, if a forecast exists for some primary forecaster (e.g. 'cramer') use it because I trust him more. If a forecast exists for some secondary forecaster (e.g. 'sothebys') use that. If no forecast exists for a category, return a row with that category and null for forecast.
I have something that almost works and after I get the logic down I hope to turn into parameterized query.
select
case when F1.category is not null
then (F1.forecaster, F1.category, F1.forecast)
when F2.category is not null
then (F2.forecaster, F2.category, F2.forecast)
else (null, C.category, null)
end
from
(
select
FR.name as forecaster,
C.id as cid,
C.category as category,
F.forecast
from
forecast F
inner join forecaster FR on (F.forecaster_id = FR.id)
inner join category C on (C.id = F.category_id)
where FR.name = 'cramer'
) F1
right join (
select
FR.name as forecaster,
C.id as cid,
C.category as category,
F.forecast
from
forecast F
inner join forecaster FR on (F.forecaster_id = FR.id)
inner join category C on (C.id = F.category_id)
where FR.name = 'sothebys'
) F2 on (F1.cid = F2.cid)
full outer join category C on (C.id = F2.cid);
This gives:
'(sothebys,wine,"bad weather, prices rise short-term")'
'(cramer,chocolate,"sell, sell, sell")'
'(cramer,autos,"demand for cocoa will skyrocket - prices up - buy, buy buy")'
'(,"real estate",)'
While that is the desired data it is a record of one column instead of three. The case was the only way I could find to achieve the ordering of cramer first sothebys next and there is lots of duplication. Is there a better way and how can the tuple like results be pulled back apart into columns?
Any suggestions, especially related to removal of duplication or general simplification appreciated.
This sounds like a case for DISTINCT ON (untested):
SELECT DISTINCT ON (c.id)
fr.name AS forecaster,
c.name AS category,
f.forecast
FROM forecast f
JOIN forecaster fr ON f.forecaster_id = fr.id
RIGHT JOIN category c ON f.category_id = c.id
ORDER BY
c.id,
CASE WHEN fr.name = 'cramer' THEN 0
WHEN fr.name = 'sothebys' THEN 1
ELSE 2
END;
For each category, the first row in the ordering will be picked. Since Cramer has a higher id than Sotheby's, it will be given preference.
Adapt the ORDER BY clause if you need a more complicated ranking.

Parsing a database column into individual columns

I'm trying to parse some data stored in a database column into individual columns. The data can vary in length. I want to name the database column the name of the value being parsed. e.g number=12345 the column should be called number, the value in the column should be 12345
An example of the data stored in a column:
id text
___________________________________________________________________________
1 Re: Fwd: number=12345:bottle=glass:water=sparkling:food=chocolate
2 number=223344:bottle=plastic:water=still:food=sweets:biscuit=digestive
What I would like is the following:
id Re Fwd number bottle water food biscuit
__________________________________________________________________
1 Re Fwd 12345 glass sparkling chocolate null
2 null null 223344 plastic still sweets digestive
I've tried (select string_to_array (text, ':') from my_table) but which splits the data but not how I want it.
table to insert to:
create table s201 (id int,Re text, Fwd text,number int, bottle text, water text, food text, biscuit text);
query:
with pre as (
with a(k,v) as (
values (1,'Re: Fwd: number=12345:bottle=glass:water=sparkling:food=chocolate'),(2,'number=223344:bottle=plastic:water=still:food=sweets:biscuit=digestive')
)
, col(s,n) as (select * from unnest(array['Re','Fwd','number','bottle','water','food','biscuit']) with ordinality c (s,n))
select s,n,k, case when o not like '%=%' then trim(o) else split_part(trim(o),'=',2) end c
from col
join a on true
left outer join unnest(string_to_array(a.v,':')) with ordinality t (o,i) on split_part(trim(o),'=',1) = s
)
, agg as (
select k, array_agg(c order by n) a
from pre
group by k
)
insert into s201
select k,a[1],a[2],a[3]::int,a[4],a[5],a[6],a[7]
from agg;
INSERT 0 2
checking:
select * from s201;
id | re | fwd | number | bottle | water | food | biscuit
----+----+-----+--------+---------+-----------+-----------+-----------
1 | Re | Fwd | 12345 | glass | sparkling | chocolate |
2 | | | 223344 | plastic | still | sweets | digestive
(2 rows)

Find all multipolygons from one table within another

So, I've got two tables - PLUTO (pieces of land), and NYZMA (rezoning boundaries). They look like:
pluto nyzma
id | geom name | geom
-------------------- -------------------
1 | MULTIPOLYGON(x) A | MULTIPOLYGON(a)
2 | MULTIPOLYGON(y) B | MULTIPOLYGON(b)
And I want it to spit out something like this, assuming that PLUTO record 1 is in multipolygons A and B, and PLUTO record 2 is in neither:
pluto_id | nyzma_id
-------------------
1 | [A, B]
2 |
How do I, for every PLUTO record's corresponding geometry, cycle through each NYZMA record, and print the names of any whose geometry matches?
Join the two tables using the spatial function ST_Contains. Than use GROUP BY and ARRAY_AGG in the main query:
WITH subquery AS (
SELECT pluto.id, nyzma.name
FROM pluto LEFT OUTER JOIN nyzma
ON ST_Contains(nyzma.geom, pluto.geom)
)
SELECT id, array_agg(name) FROM subquery GROUP BY id;

Select query for selecting columns from those records from the inner query . where inner query and outer query have different columns

I have a group by query which fetches me some records. What if I wish to find other column details representing those records.
Suppose I have a query as follows .Select id,max(date) from records group by id;
to fetch the most recent entry in the table.
I wish to fetch another column representing those records .
I want to do something like this (This incorrect query is just for example) :
Select type from (Select id,max(date) from records group by id) but here type doesnt exist in the inner query.
I am not able to define the question in a simpler manner.I Apologise for that.
Any help is appreciated.
EDIT :
Column | Type | Modifiers
--------+-----------------------+-----------
id | integer |
rdate | date |
type | character varying(20) |
Sample Data :
id | rdate | type
----+------------+------
1 | 2013-11-03 | E1
1 | 2013-12-12 | E1
2 | 2013-12-12 | A3
3 | 2014-01-11 | B2
1 | 2014-01-15 | A1
4 | 2013-12-23 | C1
5 | 2014-01-05 | C
7 | 2013-12-20 | D
8 | 2013-12-20 | D
9 | 2013-12-23 | A1
While I was trying something like this (I'm no good at sql) : select type from records as r1 inner join (Select id,max(rdate) from records group by id) r2 on r1.rdate = r2.rdate ;
or
select type from records as r1 ,(Select id,max(rdate) from records group by id) r2 inner join r1 on r1.rdate = r2.rdate ;
You can easily do this with a window function:
SELECT id, rdate, type
FROM (
SELECT id, rdate, type, rank() OVER (PARTITION BY id ORDER BY rdate DESC) rnk
FROM records
WHERE rnk = 1
) foo
ORDER BY id;
The window definition OVER (PARTITION BY id ORDER BY rdate DESC) takes all records with the same id value, then sorts then from most recent to least recent rdate and assigns a rank to each row. The rank of 1 is the most recent, so equivalent to max(rdate).
If I've understood the question right, then this should work (or at least get you something you can work with):
SELECT
b.id, b.maxdate, a.type
FROM
records a -- this is the records table, where you'll get the type
INNER JOIN -- now join it to the group by query
(select id, max(rdate) as maxdate FROM records GROUP BY id) b
ON -- join on both rdate and id, otherwise you'll get lots of duplicates
b.id = a.id
AND b.maxdate = a.rdate
Note that if you have records with different types for the same id and rdate combination you'll get duplicates.