PostgreSQL Crosstab Select Query - sql

Does any one know how to create crosstab queries in PostgreSQL?
For example I have two following tables:
TABLE A
| ID 1 | ID 2 | ID 3 |
|:-----------|------------:|:------------:|
| 00001 | 01 | 0001 |
| 00001 | 02 | 0001 |
| 00001 | 01 | 0002 |
TABLE B
| ID 1 | ID 2 | ID 3 | price | tax_rate |
|:-----------|------------:|:------------:|:------------:|:------------:|
| 00001 | 01 | 0001 |5000 | 8 |
| 00001 | 01 | 0001 |6000 | 10 |
I would like the query to return the following crosstab:
| ID 1 | ID 2 | ID 3 | price_8 | price_10 |
|:-----------|------------:|:------------:|:------------:|:------------:|
| 00001 | 01 | 0001 |5000 | 6000 |
| 00001 | 02 | 0001 |null | null |
| 00001 | 01 | 0002 |null | null |
Is this possible?

Try this -
SELECT * FROM crosstab(
'SELECT A.ID1, A.ID2, A.ID3, B.PRICE, B.TAX_RATE
FROM A
LEFT JOIN B
ON A.ID1 = B.ID1
AND A.ID2 = B.ID2
AND A.ID3 = B.ID3') AS
FINAL_RESULT (ID1 TEXT, ID2 TEXT, ID3 TEXT, PRICE_8 NUMERIC, PRICE_9 NUMERIC);

Here is a sample for your tables:
SELECT split_part(id, '.', 1) AS id1,
split_part(id, '.', 2) AS id2,
split_part(id, '.', 3) AS id3,
price_8, price_10
FROM crosstab(
'select id1||''.''||id2||''.''||id3 as id, cast(tax_rate as text) as taxRate, price
from (select * from Table1 natural left join Table2
order by 1,2,3) t'
)
AS ct (
id text,
price_8 int,
price_10 int
);

Related

SQL - Join two tables and conditionally select rows based on value from a categorical column

I have two tables with inner join. I have to conditionlly select only one row from right table based on the existence of a value in categorical column.
Condition: If blue exists, select blue else select green
Table-A:
| ID | Name |
| ---| ------|
| 01 | row |
| 02 | row |
| 03 | row |
Table-B:
| ID | CatCol |
| ---| --------|
| 01 | blue |
| 01 | green |
| 01 | red |
| 02 | green |
| 02 | red |
| 03 | blue |
Expected:
| ID | CatCol |
| ---| --------|
| 01 | blue |
| 02 | green |
| 03 | blue |
Consider below approach
select a.ID, string_agg(CatCol, '' order by if(CatCol = 'blue', 1, 2) limit 1) CatCol
from table_a a left join table_b b
on a.ID = b.ID and CatCol in ('blue', 'green')
group by ID
if applied to sample data in your question - output is
This should work for you:
SELECT b.ID, MIN(b.CatCol) as CatCol
FROM table_b b
INNER JOIN table_a a ON
b.id = a.id
GROUP BY b.ID

SQL query to get counts from two tables

I need help to write a query to combine two tables in order to get desired result set with count of rows.
Please see below in detail:
I have two tables
Table A
| CompanyID | ProductID | ProductPartsID |
| --------- | --------- | -------------- |
| 123 | ert | q1234 |
| 234 | dfr | u678 |
| 234 | dfr | Jdsdj |
| 234 | dfr | Eewe |
| 234 | dfr | dssd |
| 234 | HJU | iui89 |
| 234 | HJU | dfsfs |
| 675 | gfd | 654 |
| 675 | tyh | 765 |
Table B
|CompanyID | ProductID |
|--------- |-----------|
| 123 | ert |
| 234 | dfr |
| 234 | HJU |
| 675 | gfd |
| 709 | tgh |
| 780 | 789 |
Resultset
Both tables has millions of records in reality.
|CompanyID | #ofTableAProductPartsID | #ofTableBProductPartsID |
|--------- |-------------------------|-------------------------|
| 123 | 1 | 1 |
| 234 | 6 | 2 |
| 675 | 2 | 1 |
e.t.c
Table B does not have ProductPartsID, but it can be taken from TableA for ProductID from TableB.
using left join and group by :
SELECT
t.CompanyID
, COUNT(t.ProductPartsID)
, COUNT(DISTINCT t2.ProductID)
FROM
TABLE1 AS t
LEFT JOIN Table2 t2
ON t.CompanyID = t2.CompanyID
AND t.ProductID = t2.ProductID
GROUP BY
t.CompanyID
with TableAPartsCount as
(
select
A.CompanyID,
count(A.ProductID) as #ofTableAProductPartsID
from TableA A
group by A.CompanyID
)
,TableBPartsCount as
(
select
CompanyID,
count(ProductID) as #ofTableBProductPartsID
from TableB
group by CompanyID
)
select
A.CompanyID,
A.#ofTableAProductPartsID,
B.#ofTableBProductPartsID
from TableBPartsCount B
join TableAPartsCount A on
A.CompanyID = B.CompanyID

Calculate a column value backwards over a series of previous rows/RECURSIVE/CONNECTED BY

need your help. I guess/hope there is a function for that. I found "CONNECT DBY" and "WITH RECURSIVE AS ..." but it doesn't seem to solve my problem.
GIVEN TABLES:
Table A
+------+------------+----------+
| id | prev_id | date |
+------------------------------+
| 1 | | 20200101 |
| 23 | 1 | 20200104 |
| 34 | 23 | 20200112 |
| 41 | 34 | 20200130 |
+------------------------------+
Table B
+------+-----------+
| ref_id | key |
+------------------+
| 41 | abc |
+------------------+
(points always to the lates entry in table "A". Update, no history)
Join Statement:
SELECT
id, prev_id, key, date
FROM A
LEFT OUTER JOIN B ON B.ref_id = A.id
GIVEN psql result set:
+------+------------+----------+-----------+
| id | prev_id | key | date |
+------------------------------+-----------+
| 1 | | | 20200101 |
| 23 | 1 | | 20200104 |
| 34 | 23 | | 20200112 |
| 41 | 34 | abc | 20200130 |
+------------------------------+-----------+
DESIRED output:
+------+------------+----------+-----------+
| id | prev_id | key | date |
+------------------------------+-----------+
| 1 | | abc | 20200101 |
| 23 | 1 | abc | 20200104 |
| 34 | 23 | abc | 20200112 |
| 41 | 34 | abc | 20200130 |
+------------------------------+-----------+
The rows of the result set are connected by columns 'id' and 'prev_id'.
I want to calculate the "key" column in a reasonable time.
Keep in mind, this is a very simplified example. Normally there are a lot of more rows and different keys and id's
I understand that you want to bring the hierarchy of each row in tableb. Here is one approach using a recursive query:
with recursive cte as (
select a.id, a.prev_id, a.date, b.key
from tablea a
inner join tableb b on b.ref_id = a.id
union all
select a.id, a.prev_id, a.date, c.key
from cte c
inner join tablea a on a.id = c.prev_id
)
select * from cte

MyBatis SELECT query with IF condition

I have a table sample_table like this
+-------+-------+-------+-------+-------------+
| pkey1 | pkey2 | mode | type | type_number |
+-------+-------+-------+-------+-------------+
| 001 | 01 | light | type1 | 1234 |
| 001 | 02 | light | type2 | 2345 |
| 002 | 01 | dark | type1 | 3456 |
| 002 | 02 | dark | type2 | 4567 |
+-------+-------+-------+-------+-------------+
I have a MyBatis SELECT query pseudo-code like this
SELECT
Master.pkey1,
Master.pkey2,
Master.mode,
Master.type,
T1.selectedNumber type_number
FROM
( SELECT * from sample_table)
as Master
left join (select type_number as selectedNumber from sample_table where type='type1') as T1
ON T1.pkey1 = Master.pkey1
left join (select type_number as selectedNumber from sample_table where type='type2') as T2
ON T2.pkey1 = Master.pkey1)
Is there a way to select from T1 or T2 based on the value in mode, like
SELECT
Master.pkey1,
Master.pkey2,
Master.mode,
Master.type,
if Master.type='light'
T1.selectedNumber type_number
if Master.type='dark'
T2.selectedNumber type_number
My expected result is something like this
+-------+-------+-------+-------+-------------+
| pkey1 | pkey2 | mode | type | type_number |
+-------+-------+-------+-------+-------------+
| 001 | 01 | light | type1 | 1234 |
| 001 | 02 | light | type2 | 1234 |
| 002 | 01 | dark | type1 | 4567 |
| 002 | 02 | dark | type2 | 4567 |
+-------+-------+-------+-------+-------------+
Edit: Added Some extra code and the expected result
How about just using window functions?
select s.*,
(case when mode = 'light'
then max(case when type = 'type1' then type_number end) over (partition by pkey1)
else max(case when type = 'type2' then type_number end) over (partition by pkey2)
end)
from sample_table s;

Comparing two numbers strings and getting out the unique values

Here is a sample table I made to better illustrate my problem:
Create Table SampleTable(
TableID int,
NumberRow nvarchar(500)
)
Insert into SampleTable Values(1, '15,21,23,41,44,5,50,59,6,')
Insert into SampleTable Values(2, '10,24,29,41,5,50,59,6,73,')
Insert into SampleTable Values(3, '10,15,21,24,29,33,41,50,59,60,61,62,66,73,')
Insert into SampleTable Values(4, '10,15,21,24,28,33,37,41,44,5,50,6,60,61,62,66,')
Insert into SampleTable Values(5, '15,24,33,41,5,6,61,62,66,73,')
TableID NumberRow
---------------------------------
1 15,21,23,41,44,5,50,59,6,
2 10,24,29,41,5,50,59,6,73,
3 10,15,21,24,29,33,41,50,59,60,61,62,66,73,
4 10,15,21,24,28,33,37,41,44,5,50,6,60,61,62,66,
5 15,24,33,41,5,6,61,62,66,73,
After that I wrote a self join query:
Select
t1.TableID AS ID1,
t2.TableID AS ID2,
t1.NumberRow AS Numbers1,
t2.NumberRow AS Numbers2
From SampleTable t1
inner join SampleTable t2
on t1.TableID = t2.TableID - 1
Order by t2.TableID asc
Which results in:
ID1 ID2 Numbers1 Numbers2
-------------------------------------------------------------------------------------
1 2 15,21,23,41,44,5,50,59,6, 10,24,29,41,5,50,59,6,73,
2 3 10,24,29,41,5,50,59,6,73, 10,15,21,24,29,33,41,50,59,60,61,62,66,73,
3 4 10,15,21,24,29,33,41,50,59,60,61,62,66,73, 10,15,21,24,28,33,37,41,44,5,50,6,60,61,62,66,
4 5 10,15,21,24,28,33,37,41,44,5,50,6,60,61,62,66, 15,24,33,41,5,6,61,62,66,73,
Now I want to make two columns that show a string of numbers that are unique to both of the columns (Numbers1 and Numbers2).
So far I haven't come up with any solutions. My other approach was to make numbers be in a column instead of a string, but I still couldn't figure out how I could resolve my problem.
Maybe this query can be helpful but I agree with the design considerations about your table
select DISTINCT value as NUMBER1 ,'' AS NUMBER2 from (
Select
t1.TableID AS ID1,
t2.TableID AS ID2,
t1.NumberRow AS Numbers1,
t2.NumberRow AS Numbers2
From SampleTable t1
inner join SampleTable t2
on t1.TableID = t2.TableID - 1
) as tmp_tbl
CROSS APPLY string_split(tmp_tbl.Numbers1,',')
UNION ALL
select DISTINCT '', value from (
Select
t1.TableID AS ID1,
t2.TableID AS ID2,
t1.NumberRow AS Numbers1,
t2.NumberRow AS Numbers2
From SampleTable t1
inner join SampleTable t2
on t1.TableID = t2.TableID - 1
) as tmp_tbl
CROSS APPLY string_split(tmp_tbl.Numbers1,',')
+---------+---------+
| NUMBER1 | NUMBER2 |
+---------+---------+
| | |
| 10 | |
| 15 | |
| 21 | |
| 23 | |
| 24 | |
| 28 | |
| 29 | |
| 33 | |
| 37 | |
| 41 | |
| 44 | |
| 5 | |
| 50 | |
| 59 | |
| 6 | |
| 60 | |
| 61 | |
| 62 | |
| 66 | |
| 73 | |
| | |
| | 10 |
| | 15 |
| | 21 |
| | 23 |
| | 24 |
| | 28 |
| | 29 |
| | 33 |
| | 37 |
| | 41 |
| | 44 |
| | 5 |
| | 50 |
| | 59 |
| | 6 |
| | 60 |
| | 61 |
| | 62 |
| | 66 |
| | 73 |
+---------+---------+