aggregate functions select - sql

There is a tbl_Regist table:
| ID | CODE | VAL |
+----+--------+-----------+
| 95 | numVz | 06 |
| 95 | prevVz | 84605898 |
| 95 | ndatVz | 10.10.10 |
| 95 | numVz | 06 |
| 95 | prevVz | 14666641 |
| 95 | ndatVz | 11.11.11 |
| 95 | numVz | 06 |
| 95 | prevVz | 84605898 |
| 95 | ndatVz | 12.12.12 |
I need to get the data in this view:
| ID | numVz | prevVz | ndatVz |
+----|-------+----------+----------+
| 95 | 06 | 84605898 | 10.10.10 |
| 95 | 06 | 14666641 | 11.11.11 |
| 95 | 06 | 84605898 | 12.12.12 |
My request:
select
t.ID,
max(case when t.code = 'numVz' then t.val end) as numVz
,max(case when t.code = 'prevVz' then t.val end) as prevVz
,max(case when t.code = 'ndatVz' then t.val end) as ndatVz
from tbl_Regist t
where t.ID = 95
group by t.ID
| ID | numVz | prevVz | ndatVz |
+----|-------+----------+----------+
| 95 | 06 | 84605898 | 12.12.12 |
Returns only one row, If you remove the aggregate function, it returns 9 rows

You can use row_number() to add an enumeration. However, you really want an ordering column:
select r.ID,
max(case when r.code = 'numVz' then r.val end) as numVz,
max(case when r.code = 'prevVz' then r.val end) as prevVz,
max(case when r.code = 'ndatVz' then r.val end) as ndatVz
from (select r.*,
row_number() over (partition by id, code order by <ordering column>) as seqnum
from tbl_Regist r
) r
where r.ID = 95
group by r.ID, seqnum;

Related

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;

Transpose group of rows into multiple columns

I'm trying to transpose group of rows into multiple columns.
So far i've been able to aggregate a group of rows into a single column using for xml path, but I need to preserve further data into more columns.
CntTyp table (contact type)
| ContactID | CatCode | CatDesc |
|-----------|---------|---------|
| 89 | 26 | OA |
| 89 | 27 | OA2 |
| 90 | 26 | OA |
| 91 | 26 | OA |
| 91 | 1625 | Donor |
| 91 | 1625 | Player |
Desired Output
| ContactID | CatCode | CatDesc | CatCode | CatDesc | CatCode | CatDesc |
|-----------|---------|---------|---------|---------|---------|---------|
| 89 | 26 | OA | 27 | OA2 | | |
| 90 | 26 | OA | | | | |
| 91 | 26 | OA | 1625 | Donor | 234 | Player |
My Code:
select ContactID, catInfo =
STUFF((select ','+cast(t1.CatCode as varchar)
from CntTyp t1 where t.ContactID = t1.ContactID
for xml path ('')), 1, 1, '')
from CntTyp t
group by ContactID
My Output
| ContactID | catInfo |
|-----------|-------------|
| 89 | 26,27 |
| 90 | 26 |
| 91 | 26,1625,234 |
We can try doing a pivot query with the help of ROW_NUMBER:
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ContactID ORDER BY CatCode, CatDesc) rn
FROM CntTyp
)
SELECT
ContactID,
MAX(CASE WHEN rn = 1 THEN CatCode END) AS CatCode1,
MAX(CASE WHEN rn = 1 THEN CatDesc END) AS CatDesc1,
MAX(CASE WHEN rn = 2 THEN CatCode END) AS CatCode2,
MAX(CASE WHEN rn = 2 THEN CatDesc END) AS CatDesc2,
MAX(CASE WHEN rn = 3 THEN CatCode END) AS CatCode3,
MAX(CASE WHEN rn = 3 THEN CatDesc END) AS CatDesc3
FROM cte
GROUP BY
ContactID;

propagate hierarchical values from a single column

I have table with structure like this, which represents status changes.
+-----------+----------+----------------+-----------------+-------+
| record_id | group_id | attribute type | change date | value |
+-----------+----------+----------------+-----------------+-------+
| 1 | 1 | status | 4/16/2008 18:59 | s1 |
| 2 | 1 | details | 4/16/2008 18:59 | d5 |
| 3 | 1 | details | 8/7/2008 18:31 | d2 |
| 4 | 1 | details | 2/5/2009 22:15 | d1 |
| 5 | 1 | status | 4/3/2009 21:27 | s2 |
| 6 | 1 | details | 4/3/2009 21:27 | d7 |
| 7 | 2 | status | 4/3/2009 21:46 | s1 |
| 8 | 2 | details | 4/3/2009 21:46 | d1 |
+-----------+----------+----------------+-----------------+-------+
I'd like to query status changes and status details changes in two columns, grouped by time stamp (actually any status change makes change to details, so only details change timestamp could be used for easier grouping) and status propagated to related details, like this:
+-----------+-----------------+--------+---------+
| object id | change date | status | details |
+-----------+-----------------+--------+---------+
| 1 | 4/16/2008 18:59 | s1 | d5 |
| 1 | 8/7/2008 18:31 | s1 | d2 |
| 1 | 2/5/2009 22:15 | s1 | d1 |
| 1 | 4/3/2009 21:27 | s2 | d3 |
| 2 | 4/3/2009 21:46 | s1 | d1 |
+-----------+-----------------+--------+---------+
this is what I've started with, but it leaves me with NULLs
SELECT history.record_id,
history.group_id,
history.changedate,
status_chages.value AS status,
history.value AS details
FROM history
LEFT JOIN (SELECT
history.group_id,
history.changedate,
history.value
FROM history
WHERE history.attribute_type = 'status') status_chages
ON status_chages.group_id = history.group_id AND
status_chages.changedate = history.changedate
WHERE history.attribute = 'details'
First thing which came to my mind is to fill NULLs with previous row data.
But is there a better approach for querying the result listed above?
The query gives desired layout:
select
group_id,
change_date,
max(case attr_type when 'status' then value else null end) as status,
max(case attr_type when 'status' then null else value end) as detail
from history
group by 1, 2
order by 1, 2;
group_id | change_date | status | detail
----------+---------------------+--------+--------
1 | 2008-04-16 18:59:00 | s1 | d5
1 | 2008-08-07 18:31:00 | | d2
1 | 2009-02-05 22:15:00 | | d1
1 | 2009-04-03 21:27:00 | s2 | d7
2 | 2009-04-03 21:46:00 | s1 | d1
(5 rows)
You can fill nulls with previous values in such a way:
select
group_id,
change_date,
max(status) over (partition by group_id, part) status,
detail
from (
select *, count(status) over (partition by group_id order by change_date) part
from (
select
group_id,
change_date,
max(case attr_type when 'status' then value else null end) as status,
max(case attr_type when 'status' then null else value end) as detail
from history
group by 1, 2
) s
) s
order by 1, 2;
group_id | change_date | status | detail
----------+---------------------+--------+--------
1 | 2008-04-16 18:59:00 | s1 | d5
1 | 2008-08-07 18:31:00 | s1 | d2
1 | 2009-02-05 22:15:00 | s1 | d1
1 | 2009-04-03 21:27:00 | s2 | d7
2 | 2009-04-03 21:46:00 | s1 | d1
(5 rows)
Test it in rextester.

Select values of a column into one row - SQL Server

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

SQL Server pivot with "ties"

Here is my source data:
+-------+-------+-------+------+
| Categ | Nm | Value | Rnk |
+-------+-------+-------+------+
| A | Tom | 37 | 1 |
| A | Joe | 36 | 2 |
| A | Eddie | 35 | 3 |
| B | Seth | 28 | 1 |
| B | Ed | 25 | 2 |
| B | Billy | 22 | 3 |
| C | Julie | 42 | 1 |
| C | Jenny | 41 | 2 |
| C | April | 40 | 3 |
| C | Mary | 40 | 3 |
| C | Laura | 40 | 3 |
+-------+-------+-------+------+
And here is the output I would like to produce:
+------+--------+--------+-------+
| Rnk | A | B | C |
+------+--------+--------+-------+
| 1 | Tom | Seth | Julie |
| 2 | Joe | Ed | Jenny |
| 3 | Eddie | Billy | April |
| 3 | (null) | (null) | Mary |
| 3 | (null) | (null) | Laura |
+------+--------+--------+-------+
I have used the following approach (which I understand through other posts may be superior to actually using PIVOT)...and this gets me to where I see Julie/Jenny/April, but not Mary/Laura (obviously, since it is pulling the MIN in the event of a 'tie').
SELECT Rnk
, min(CASE WHEN Categ = 'A' THEN Nm END) as A
, min(CASE WHEN Categ = 'B' THEN Nm END) as B
, min(CASE WHEN Categ = 'C' THEN Nm END) as C
FROM Tbl
GROUP BY Rnk
How to get to my desired output?
Well, if you want multiple rows for each rank, you can't aggregate by rank, or at least by rank alone. So, calculate the rank-within-the-rank or as the following query calls it, the sub_rnk:
SELECT Rnk,
min(CASE WHEN Categ = 'A' THEN Nm END) as A,
min(CASE WHEN Categ = 'B' THEN Nm END) as B,
min(CASE WHEN Categ = 'C' THEN Nm END) as C
FROM (select t.*, row_number() over (partition by categ, rnk order by newid()) as sub_rnk
from Tbl t
) t
GROUP BY rnk, sub_rnk
ORDER BY rnk;