SQL: aggregate & transpose rows to columns - sql

I am trying to transpose the data from the first table to the second.
original data (number of cars and states are limited):
+----+----------+-------+--------+
| id | car | state | tstamp |
+----+----------+-------+--------+
| 01 | toyota | new | 1900 |
| 02 | toyota | old | 1950 |
| 03 | toyota | scrap | 1980 |
| 04 | mercedes | new | 1990 |
| 05 | mercedes | old | 2010 |
| 06 | tesla | new | 2013 |
+-----+---------------+----------+
query result:
+----------+------+------+-------+
| car | new | old | scrap |
+----------+------+------+-------+
| toyota | 1900 | 1950 | 1980 |
| mercedes | 1990 | 2010 | null |
| tesla | 2013 | null | null |
+----------+------+------+-------+
My SQL Skills are somewhat rusty therefore I would appreciate any help!

Something like this would work, depending on how your data is organised:
SELECT
car,
MAX(CASE WHEN state = 'new' THEN tstamp END) AS new,
MAX(CASE WHEN state = 'old' THEN tstamp END) AS old,
MAX(CASE WHEN state = 'scrap' THEN tstamp END) AS scrap
FROM
table
GROUP BY
car;

Related

SQL Server : select distinct data with transpose

I have data in a table like this:
+-----------+-------------+-----------------+------+----------+
| make | model | variant | year | price |
+-----------+-------------+-----------------+------+----------+
| CHEVROLET | SPARK | 08LS | 2018 | 1000000 |
| CHEVROLET | SPARK | 08LS | 2017 | 2000000 |
| CHEVROLET | SPARK | 08LS | 2016 | 3000000 |
| CHEVROLET | SPARK | 08LSTRENDY | 2018 | 4000000 |
| CHEVROLET | SPARK | 08LSTRENDY | 2017 | 5000000 |
| CHEVROLET | SPARK | 08LSTRENDY | 2016 | 6000000 |
| TOYOTA | LANDCRUISER | 10042DVX | 2018 | 7000000 |
| TOYOTA | LANDCRUISER | 10042DVX | 2017 | 8000000 |
| TOYOTA | LANDCRUISER | 10042DVX | 2016 | 9000000 |
| TOYOTA | LANDCRUISER | 10042DVX | 2015 | 10000000 |
| TOYOTA | LANDCRUISER | 10042DVXLIMITED | 2018 | 11000000 |
| TOYOTA | LANDCRUISER | 10042DVXLIMITED | 2017 | 12000000 |
| TOYOTA | LANDCRUISER | 10042DVXLIMITED | 2016 | 13000000 |
| TOYOTA | LANDCRUISER | 10042DVXLIMITED | 2015 | 14000000 |
| TOYOTA | LANDCRUISER | 10042DVXLIMITED | 2014 | 15000000 |
| TOYOTA | LANDCRUISER | 10042DVXLIMITED | 2013 | 16000000 |
+-----------+-------------+-----------------+------+----------+
I want to select data in below form
+-----------+-------------+-----------------+----------+----------+----------+----------+----------+----------+
| make | model | variant | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 |
+-----------+-------------+-----------------+----------+----------+----------+----------+----------+----------+
| CHEVROLET | SPARK | 08LS | 1000000 | 2000000 | 3000000 | NULL | NULL | NULL |
| CHEVROLET | SPARK | 08LSTRENDY | 4000000 | 5000000 | 6000000 | NULL | NULL | NULL |
| TOYOTA | LANDCRUISER | 10042DVX | 7000000 | 8000000 | 9000000 | 10000000 | NULL | NULL |
| TOYOTA | LANDCRUISER | 10042DVXLIMITED | 11000000 | 12000000 | 13000000 | 14000000 | 15000000 | 16000000 |
+-----------+-------------+-----------------+----------+----------+----------+----------+----------+----------+
Could you please help me to write a query for this?
You can do conditional aggregation :
select make, model, variant,
sum(case when yr = 2018 then price else 0 end),
sum(case when yr = 2017 then price else 0 end),
sum(case when yr = 2016 then price else 0 end),
sum(case when yr = 2015 then price else 0 end)
from table t
group by make, model, variant;

Grouping by a column to compare values between similar rows

I'm trying to turn this
+----+---------+-------------------+-----------+
| id | year | desc | amount |
+----+---------+-------------------+-----------+
| 1 | 2017 | car | 500 |
| 2 | 2017 | car | 550 |
| 1 | 2018 | car | 490 |
| 2 | 2018 | car | 550 |
| 1 | 2017 | house | 200 |
| 2 | 2017 | house | 300 |
| 1 | 2018 | house | 210 |
| 2 | 2018 | house | 320 |
| 1 | 2019 | house | 290 |
| 2 | 2019 | house | 325 |
+----+---------+-------------------+-----------+
Into something like this
+----+---------+---------+-------------------+-----------+-----------+
| id | year_0 | year_1 | desc | amount_0 | amount_1 |
+----+---------+---------+-------------------+-----------+-----------+
| 1 | 2017 | 2018 | car | 500 | 490 |
| 2 | 2017 | 2018 | car | 550 | 550 |
| 1 | 2017 | 2018 | house | 200 | 210 |
| 2 | 2017 | 2018 | house | 300 | 320 |
+----+---------+---------+-------------------+-----------+-----------+
But I'm having difficulty getting the two years and two amounts to group by description.
You can achieve the result by applying join:
SELECT A.id,a.year year_0,b.year year_1, A.[desc], A.amount amount_0,B.amount amount_1
FROM
(SELECT * FROM YourTable WHERE Year= Datepart(year,GETDATE())-1) AS A
INNER JOIN
(SELECT * FROM YourTable WHERE Year= Datepart(year,GETDATE())) AS B
ON A.id=B.id AND A.[desc]=B.[desc]

Creating SQL Query references a few tables

I tried to do this myself with views but I unfortunately I don't have the skills or knowledge required.
I've put in some dummy data to demonstrate.
uri of course = object_uri
TABLE_RECORD
+-----+---------------+---------+
| uri | title | client |
+-----+---------------+---------+
| 1 | australia | peter |
| 2 | new zealand | peter |
| 3 | canada | chris |
| 4 | united states | mitch |
| 5 | ireland | michael |
| 6 | scotland | mitch |
+-----+---------------+---------+
TABLE_UDF
+------------+--------------+----------------+
| object_uri | udf_type_uri | udf_type_value |
+------------+--------------+----------------+
| 1 | 2005 | 1/12/2007 |
| 2 | 2005 | 2/04/2008 |
| 2 | 2006 | 3/04/2009 |
| 3 | 2005 | 4/05/2010 |
| 4 | 2006 | 12/04/2016 |
| 5 | 2005 | 14/05/2005 |
| 5 | 2006 | 14/05/2006 |
| 6 | 2005 | 20/01/2017 |
+------------+--------------+----------------+
EXPECTED OUTPUT
+-----+---------------+---------+------------+------------+
| uri | title | client | udf_type_1 | udf_type_2 |
+-----+---------------+---------+------------+------------+
| 1 | australia | peter | 1/12/2007 | |
| 2 | new zealand | peter | 2/04/2008 | 3/04/2009 |
| 3 | canada | chris | 4/05/2010 | |
| 4 | united states | mitch | | 12/04/2016 |
| 5 | ireland | michael | 14/05/2005 | 14/05/2006 |
| 6 | scotland | mitch | 20/01/2017 | |
+-----+---------------+---------+------------+------------+
Thanks heaps in advanced.
If I understand correctly, this is a join with conditional aggregation:
select r.uri, r.title, r.client,
max(case when u.udf_type_uri = 2005 then udf_type_value end) as udf_type_1,
max(case when u.udf_type_uri = 2006 then udf_type_value end) as udf_type_2
from record r join
udf u
on r.uri = u.object_uri
group by r.uri, r.title, r.client;
SELECT
*
FROM
table_record
LEFT JOIN
(
SELECT
object_uri,
MAX(CASE WHEN udf_type_uri = 2005 THEN udf_type_value END) AS udf_type_1,
MAX(CASE WHEN udf_type_uri = 2006 THEN udf_type_value END) AS udf_type_2
FROM
table_udf
GROUP BY
object_uri
)
table_udf
ON table_udf.object_uri = table_record.uri
The inner query squashes the udf table down to one row per uri, and uses MAX() and CASE to ensure the correct udf is placed in the correct column. Then you just join the tables as normal.
(Can also be done using PIVOT, but that's always seems clunkier to me...)
https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx

Postgres: select n unique rows for ID

Using Postgres I have a scenario where I need to return a variable number of rows for a each unique id in a sql statement.
Consider I have a table of the cars a user has owned over the years.
+----+----------+---------+-------+
| ID | make | model | type |
+----+----------+---------+-------+
| 1 | toyota | camry | sedan |
| 1 | ford | mustang | coupe |
| 1 | toyota | celica | coupe |
| 1 | bmw | z4 | coupe |
| 1 | honda | accord | sedan |
| 2 | buick | marque | sedan |
| 2 | delorean | btf | coupe |
| 2 | mini | cooper | coupe |
| 3 | ford | f-150 | truck |
| 3 | ford | mustang | coupe |
| 1 | ford | taurus | sedan |
+--------+----------+-------+-----+
From this table I'd only want to return two rows for each user that has a coupe and ignore the rest.
So something like. I'd also like to preserve the empty columns so the second result for ID 3 would be empty because there is only one car of type coupe. I am also working with restrictions as this has to run AWS Reshift. So, I can't use many functions. It seems this would be easy using a Top statement like in SQL server, but with Redshift restrictions and my lack of knowledge I'm not sure of the best way.
+----+----------+---------+-------+
| ID | make | model | type |
+----+----------+---------+-------+
| 1 | ford | mustang | coupe |
| 1 | toyota | celica | coupe |
| 2 | delorean | btf | coupe |
| 2 | mini | cooper | coupe |
| 3 | ford | mustang | coupe |
| 3 | | | |
+--------+----------+-------+-----+
Thanks a lot for your help.
As far as I know, Redshift supports window functions:
select id, make, model, type
from (
select id, make, model, type,
row_number() over (partition by id order by make) as rn
from the_table
where type = 'coupe'
) t
where rn <= 2
order by id, make;

Remove duplicate column value

Considering my table
+----+------+--------+----------+
| ID | Name | Salary | Month |
+----+------+--------+----------+
| 1 | a | 5000 | Jan |
| 2 | b | 5500 | Jan |
| 3 | b | 5300 | Feb |
| 4 | b | 5300 | Mar |
| 5 | b | 5300 | Apr |
| 6 | b | 5300 | May |
| 7 | b | 5300 | June |
| 8 | b | 5300 | July |
+----+------+--------+----------+
I need to display
+----+------+--------+----------+
| ID | Name | Salary | Month |
+----+------+--------+----------+
| 1 | a | 5000 | Jan |
| 2 | b | 5500 | Jan |
| 3 | | 5300 | Feb |
| 4 | | 5300 | Mar |
| 5 | | 5300 | Apr |
| 6 | | 5300 | May |
| 7 | | 5300 | June |
| 8 | | 5300 | July |
+----+------+--------+----------+
Can any1 help
I'm assuming the results are sorted by the name column (and probably then the salary?), you could use the analytic function ROW_NUMBER to single out the first one, and only print that one out.
You may want to tweak the query around a bit, but here's the main idea:
SELECT id, CASE rn WHEN 1 THEN name ELSE null END, salary, month
FROM (SELECT id, name, salary, month,
ROW_NUMBER() OVER (PARTITION BY name ORDER BY salary) AS rn
FROM some_table)
ORDER BY name, rn
Please try below query for MS Sql server:
select
a.ID,
case when a.Name=b.Name then NULL else a.Name end Name,
a.Salary,
a.[Month]
from YourTable a left join YourTable b
on a.ID=b.ID+1