Select values of a column into one row - SQL Server - sql

I want to select in one row the value of a column that appears in multiple rows, I have the table Solution:
| StudentID | SolutionDate | SolutionTime | SongID |
----------------------------------------------------
| 0824616 | 2015-09-20 | 00:07:00 | 01 |
| 0824616 | 2015-09-20 | 00:05:00 | 02 |
| 0824616 | 2015-09-21 | 00:07:40 | 01 |
| 0824616 | 2015-09-21 | 00:10:00 | 03 |
| 0824616 | 2015-09-23 | 00:04:30 | 03 |
| 0824616 | 2015-09-23 | 00:11:30 | 03 |
I want to group the records by StudentID and SongID.
The expected output is:
| StudentID | SongID | TimeA | TimeB | TimeC |
-------------------------------------------------------
| 0824616 | 01 | 00:07:00 | 00:07:40 | NULL |
| 0824616 | 02 | 00:05:00 | NULL | NULL |
| 0824616 | 03 | 00:10:00 | 00:04:30 | 00:11:30 |
There are 3 records by StudentID-SongID at the most. I'm using SQL Server 2012.

Try with window function first to number the rows and then use conditional aggregation:
;with cte as(select *, row_number() over(partition by studentid, songid
order by solutiondate, solutiontime) rn from tablename)
select studentid,
songid,
max(case when rn = 1 then solutiontime end) as timea,
max(case when rn = 2 then solutiontime end) as timeb,
max(case when rn = 3 then solutiontime end) as timec
from cte
group by studentid, songid

Related

How to get Max date and sum of its rows SQL

I have following table,
+------+-------------+----------+---------+
| id | date | amount | amount2 |
+------+-------------+----------+---------+
| | | | 500 |
| 1 | 1/1/2020 | 1000 | |
+------+-------------+----------+---------+
| | | | 100 |
| 1 | 1/3/2020 | 1558 | |
+------+-------------+----------+---------+
| | | | 200 |
| 1 | 1/3/2020 | 126 | |
+------+-------------+----------+---------+
| | | | 500 |
| 2 | 2/5/2020 | 4921 | |
+------+-------------+----------+---------+
| | | | 100 |
| 2 | 2/5/2020 | 15 | |
+------+-------------+----------+---------+
| | | | 140 |
| 2 | 1/1/2020 | 5951 | |
+------+-------------+----------+---------+
| | | | 10 |
| 2 | 1/2/2020 | 1588 | |
+------+-------------+----------+---------+
| | | | 56 |
| 2 | 1/3/2020 | 1568 | |
+------+-------------+----------+---------+
| | | | 45 |
| 2 | 1/4/2020 | 12558 | |
+------+-------------+----------+---------+
I need to get each Id's max date and its amount and amount2 summations, how can I do this. according to above data, I need following output.
+------+-------------+----------+---------+
| | | | 300 |
| 1 | 1/3/2020 | 1684 | |
+------+-------------+----------+---------+
| | | | 600 |
| 2 | 2/5/2020 | 4936 | |
+------+-------------+----------+---------+
How can I do this.
Aggregate and use MAX OVER to get the IDs' maximum dates:
select id, [date], sum_amount, sum_amount2
from
(
select
id, [date], sum(amount) as sum_amount, sum(amount2) as sum_amount2,
max([date]) over (partition by id) as max_date_for_id
from mytable group by id, [date]
) aggregated
where [date] = max_date_for_id
order by id;
first is to use dense_rank() to find the row with latest date
dense_rank () over (partition by id order by [date] desc)
after that, just simply group by with sum() on the amount
select id, [date], sum(amount), sum(amount2)
from
(
select *,
dr = dense_rank () over (partition by id order by [date] desc)
from your_table
) t
where dr = 1
group by id, [date]

Return Max Value Date for each group in Netezza SQL

+--------+---------+----------+------------+------------+
| CASEID | USER ID | TYPE | OPEN_DT | CLOSED_DT |
+--------+---------+----------+------------+------------+
| 1 | 1000 | MA | 2017-01-01 | 2017-01-07 |
| 2 | 1000 | MB | 2017-07-15 | 2017-07-22 |
| 3 | 1000 | MA | 2018-02-20 | NULL |
| 8 | 1001 | MB | 2017-05-18 | 2018-02-18 |
| 9 | 1001 | MA | 2018-03-05 | 2018-04-01 |
| 7 | 1002 | MA | 2018-06-01 | 2018-07-01 |
+--------+---------+----------+------------+------------+
This is a snippet of my data set. I need a query that returns just the max(OPEN_DT) row for each USER_ID in Netezza SQL.
so given the above the results would be:
| CASEID | USERID | TYPE | OPEN_DT | CLOSED_DT |
| 3 | 1000 | MA | 2018-02-20 | NULL |
| 9 | 1001 | MA | 2018-03-05 | 2018-04-01 |
| 7 | 1002 | MA | 2018-06-01 | 2018-07-01 |
Any help is very much appreciated!
You can use correlated subquery :
select t.*
from table t
where open_dt = (select max(t1.open_dt) from table t1 where t1.user_id = t.user_id);
You can also row_number() :
select t.*
from (select *, row_number() over (partition by user_id order by open_dt desc) as seq
from table t
) t
where seq = 1;
However if you have a ties with open_dt then you would need to use limit clause with correlated subquery but i am not sure about the ties so i just leave it.

PostgreSQL multiple row as columns

I have a table like this:
| id | name | segment | date_created | question | answer |
|----|------|---------|--------------|----------|--------|
| 1 | John | 1 | 2018-01-01 | 10 | 28 |
| 1 | John | 1 | 2018-01-01 | 14 | 37 |
| 1 | John | 1 | 2018-01-01 | 9 | 83 |
| 2 | Jack | 3 | 2018-03-11 | 22 | 13 |
| 2 | Jack | 3 | 2018-03-11 | 23 | 16 |
And I want to show this information in a single row, transpose all the questions and answers as columns:
| id | name | segment | date_created | question_01 | answer_01 | question_02 | answer_02 | question_03 | answer_03 |
|----|------|---------|--------------|-------------|-----------|-------------|-----------|-------------|-----------|
| 1 | John | 1 | 2018-01-01 | 10 | 28 | 14 | 37 | 9 | 83 |
| 2 | Jack | 3 | 2018-03-11 | 22 | 13 | 23 | 16 | | |
The number os questions/answers for the same ID is known. Maximum of 15.
I'm already tried using crosstab, but it only accepts a single value as category and I can have 2 (question/answer). Any help how to solve this?
You can try to use row_number to make a number in subquery then, do Aggregate function condition in the main query.
SELECT ID,
Name,
segment,
date_created,
max(CASE WHEN rn = 1 THEN question END) question_01 ,
max(CASE WHEN rn = 1 THEN answer END) answer_01 ,
max(CASE WHEN rn = 2 THEN question END) question_02,
max(CASE WHEN rn = 2 THEN answer END) answer_02,
max(CASE WHEN rn = 3 THEN question END) question_03,
max(CASE WHEN rn = 3 THEN answer END) answer_03
FROM (
select *,Row_number() over(partition by ID,Name,segment,date_created order by (select 1)) rn
from T
) t1
GROUP BY ID,Name,segment,date_created
sqlfiddle
[Results]:
| id | name | segment | date_created | question_01 | answer_01 | question_02 | answer_02 | question_03 | answer_03 |
|----|------|---------|--------------|-------------|-----------|-------------|-----------|-------------|-----------|
| 1 | John | 1 | 2018-01-01 | 1 | 28 | 14 | 37 | 9 | 83 |
| 2 | Jack | 3 | 2018-03-11 | 22 | 13 | 23 | 16 | (null) | (null) |

TSQL : conditional query

I am trying to find a way to get results where the Occupancy in the latest Inspection_date and the one before the last are not equal.
In this example only number RoomID 2 will be the only result because: the Occupancy for OrderID 201 = 'Vacant' <> to the Occupancy for OrderID 202 = 'Occupied'.
I have the beginning of the query but cannot seem to find a good logic to end the query.
| RoomID | OrderID | Occupancy | rn |
+--------+---------+-----------+----+
| 01 | 101 | Vacant | 1 |
| 01 | 102 | Vacant | 2 |
| 01 | 103 | Occupied | 3 |
| 01 | 104 | Vacant | 4 |
| 02 | 201 | Vacant | 1 |
| 02 | 202 | Occupied | 2 |
| 02 | 203 | Vacant | 3 |
| 03 | 301 | Occupied | 1 |
| 03 | 302 | Occupied | 2 |
| 03 | 303 | Occupied | 3 |
| 03 | 304 | Occupied | 4 |
| 04 | 401 | Occupied | 1 |
| 04 | 402 | Occupied | 2 |
| 04 | 403 | Vacant | 3 |
| 04 | 404 | Occupied | 4 |
SELECT i.room_number, order_number, Occupancy , row_number() OVER(PARTITION BY room_number ORDER BY Inspection_date DESC) rn
FROM #inspection_data i
In SQL Server 2012+, you can use lag(), so something like this:
SELECT i.*
FROM (SELECT i.room_number, order_number, Occupancy ,
ROW_NUMER() OVER (PARTITION BY room_number ORDER BY Inspection_date DESC) as seqnum,
LAG(Occupancy) OVER (PARTITION BY room_number ORDER BY Inspection_date) as prev_Occupancy
FROM #inspection_data i
) i
WHERE prev_Occupancy <> Occupancy AND seqnum = 1 ;

Creating a rank that resets on a specific value of a column

My current data looks like this (note that it is sorted on datetime):
+----------------+---------------------+---------+
| CustomerNumber | Date | Channel |
+----------------+---------------------+---------+
| 120584446 | 2015-05-22 21:16:05 | A |
| 120584446 | 2015-05-25 18:04:16 | A |
| 120584446 | 2015-05-25 18:05:25 | B |
| 120584446 | 2015-05-28 20:35:09 | A |
| 120584446 | 2015-05-28 20:36:01 | A |
| 120584446 | 2015-05-28 20:37:02 | B |
| 120584446 | 2015-05-29 13:39:00 | B |
+----------------+---------------------+---------+
I want to create a rank in hive that splits on cutomer number and whenever the channel is A. It should look like this:
+----------------+---------------------+----------------+------+
| CustomerNumber | Date | Channel | Rank |
+----------------+---------------------+----------------+------+
| 120584446 | 2015-05-22 21:16:05 | A | 1 |
| 120584446 | 2015-05-25 18:04:16 | A | 1 |
| 120584446 | 2015-05-25 18:05:25 | B | 2 |
| 120584446 | 2015-05-28 20:35:09 | A | 1 |
| 120584446 | 2015-05-28 20:36:01 | A | 1 |
| 120584446 | 2015-05-28 20:37:02 | B | 2 |
| 120584446 | 2015-05-29 13:39:00 | B | 3 |
+----------------+---------------------+----------------+------+
One approach is to use a cumulative conditional sum to identify the groups and then use row_number() for the ranking:
select t.*,
row_number() over (partition by CustomerNumber, grp
order by date
) as rank
from (select t.*,
sum(case when channel = 'A' then 1 else 0 end) over
(partition by CustomerNumber order by date) as grp
from t
) t;