merge two rows to one row with same ID sql db2 - db2

I need help with below.
This is my Table:
Id Out AccountNumber Amount Key
14587 2 32345678 458.00 Accepted
15672 7 12335678 095.00 Customer
15672 5 12345678 095.00 Description
17672 1 21345678 408.00 Accepted
15672 5 12345678 095.00 List
17672 1 12345678 408.00 Accepted
My desired output should be like:
Id Out AccountNumber Amount Item1 Item2 Item3
14587 2 32345678 458.00 Accepted NULL NULL
15672 7 12335678 095.00 Customer Description List
17672 1 21345678 408.00 Accepted Accepted NULL
Thanks in advance,
Chris

Try this:
/*
WITH TAB (ID, Out, AccountNumber, Amount, Key) AS
(
VALUES
(14587, 2, 32345678, 458.00, 'Accepted')
, (15672, 7, 12345678, 095.00, 'Customer')
, (15672, 5, 12345678, 095.00, 'Description')
, (17672, 1, 21345678, 408.00, 'Accepted')
, (15672, 5, 12345678, 095.00, 'List')
, (17672, 1, 21345678, 408.00, 'Accepted')
)
*/
SELECT
MAX(ID) ID, MAX(Out) Out, AccountNumber
, MAX(Amount) AS Amount
, MAX(CASE RN WHEN 1 THEN Key END) AS ITEM1
, MAX(CASE RN WHEN 2 THEN Key END) AS ITEM2
, MAX(CASE RN WHEN 3 THEN Key END) AS ITEM3
FROM
(
SELECT
ID, Out, AccountNumber, Amount, Key
, ROWNUMBER() OVER (PARTITION BY AccountNumber ORDER BY Key) RN
FROM TAB
)
GROUP BY AccountNumber;
You may uncomment the commented out block and run the statement as is.

Related

TSQL - Get latest rows which their title is not null

I have following table:
========================
Id SubCode Title
========================
1 1 test1
1 2 test2
1 3 NULL
1 4 NULL
2 1 k1
2 2 k2
2 3 k3
2 4 NULL
No I want to select latest rows which their title is not null, for example for Id 1 then query must show test2 and for Id 2 it must be k3:
========================
Id SubCode Title
========================
1 2 test2
2 3 k3
I have written this query:
select t.Id, t.SubCode, t.Title from Test t
inner join (
select max(Id) as Id, max(SubCode) as SubCode
from Test
group by Id
) tm on t.Id = tm.Id and t.SubCode = tm.SubCode
But this code gives the wrong result:
========================
Id SubCode Title
========================
1 4 NULL
2 4 NULL
Any idea?
You forgot to exclude NULLs by writing an appropriate WHERE clause (where title is not null).
However such problems (to get a best / last / ... record) are usually best solved with analytic functions (RANK, DENSE_RANK, ROW_NUMBER) anyway, because with them you access the table only once:
select id, subcode, title
from
(
select id, subcode, title, rank() over (partition by id order by subcode desc) as rn
from test
where title is not null
) ranked
where rn = 1;
You need a Title is not null where clause in your inner select:
select t.Id, t.SubCode, t.Title from Test t
inner join (
select max(Id) as Id, max(SubCode) as SubCode
from Test
where Title is not null
group by Id
) tm on t.Id = tm.Id and t.SubCode = tm.SubCode

GROUP BY for MAX and NULL

I want max startdate but there is a NULL data, it will be null.
Sample data is as follows:
DECLARE #Tbl TABLE (Id INT, StartDate DATETIME)
INSERT INTO #Tbl
VALUES (1, NULL),
(1, '2016.07.30'),
(1, '2016.07.05'),
(1, '2016.07.05'),
(2, '2016.07.07'),
(2, '2016.07.05'),
(3, '2016.07.05'),
(3, NULL)
My Query:
SELECT Id, MAX(StartDate) AS StartDate
FROM #Tbl
GROUP BY Id
Output:
Id StartDate
----------- ----------
1 2016-07-30
2 2016-07-07
3 2016-07-05
Desired Output:
Id StartDate
----------- ----------
1 NULL
2 2016-07-07
3 NULL
To solve this problem we can use a count function that behave different in two cases:
when we use count(*) then all rows are count (also with null value)
when we use count(someFieldName) then only rows with not null value are count
You can see this different behaviour on this example using sample data from the question
select Id, count(*) as count_all, count(StartDate) as count_StartDate
from #Tbl
group by Id;
On the output we can see this
Id count_all count_StartDate
1 4 3
2 2 2
3 2 1
We can use this different behaviour to solve problem from question by this query
select Id, case when count(*) = count(StartDate)
then max(StartDate)
else null
end as StartDate
from #Tbl
group by Id
On the output we can see the desired result
Id StartDate
1 NULL
2 2016-07-07 00:00:00.000
3 NULL
Found the result.
SELECT Id, CASE
WHEN MAX(COALESCE(StartDate, '2099.01.01')) = '2099.01.01' THEN NULL
ELSE MAX(StartDate) END AS StartDate
FROM #Tbl
GROUP BY Id

GROUP BY column and clause in postgres

I would like group the columns of a table with by a column value as well as when another condition is met. For example, with the following table:
Events:
id session_id flags created_at ...
--------------------------------------------
1 100 OTHER ...
2 101 OTHER ...
3 101 NEW_SESSION ...
4 101 OTHER ...
5 101 NEW_SESSION ...
6 100 OTHER ...
7 102 OTHER ...
I want the following result:
session_id events_count first_event_id last_event_id
-------------------------------------------------------
100-0 2 1 6
101-0 1 2 2
101-1 2 3 4
101-2 1 5 5
102-0 1 7 7
The basic idea is that I want to extract sessions from events. They are grouped by session_id. I also want a new session whenever I have the flag NEW_SESSION.
The query is something like this:
SELECT ? as session_id
, count(id) as events_count
, MIN(id) as first_event_id
, MAX(id) last_event_id
GROUP BY session_id
-- , and whenever flags is NEW_SESSION
ORDER BY id
But I dont know how to express the group by condition properly. Any idea ?
Update 2
In the comments I've noticed that you want them unique. Then we can use a variable:
SET #inc := 0;
(
SELECT CONCAT(session_id, '-', !ABS(STRCMP(flags, 'NEW_SESSION'))) AS session_id
, COUNT(id) AS events_count
, MIN(id) AS first_event_id
, MAX(id) last_event_id
FROM events
WHERE flags != 'NEW_SESSION'
GROUP BY events.session_id, events.flags
ORDER BY events.id
) UNION (
SELECT CONCAT(session_id, '-', #inc := #inc + 1) AS session_id
, COUNT(id) AS events_count
, MIN(id) AS first_event_id
, MAX(id) last_event_id
FROM events
WHERE flags = 'NEW_SESSION'
GROUP by events.id
ORDER BY events.id
);
Update
The following prevents grouping for the NEW_SESSION rows:
(
SELECT CONCAT(session_id, '-', !ABS(STRCMP(flags, 'NEW_SESSION'))) AS session_id
, COUNT(id) AS events_count
, MIN(id) AS first_event_id
, MAX(id) last_event_id
FROM events
WHERE flags != 'NEW_SESSION'
GROUP BY events.session_id, events.flags
ORDER BY events.id
) UNION (
SELECT CONCAT(session_id, '-1') AS session_id
, COUNT(id) AS events_count
, MIN(id) AS first_event_id
, MAX(id) last_event_id
FROM events
WHERE flags = 'NEW_SESSION'
GROUP BY id
ORDER BY events.id
);
Original answer
As far as I understand, you are trying to group events by the session IDs and
"whether it's a NEW_SESSION" flag. If it's so, then I'd express it as follows:
SELECT CONCAT(session_id, '-', !ABS(STRCMP(flags, 'NEW_SESSION'))) AS session_id
, COUNT(id) AS events_count
, MIN(id) AS first_event_id
, MAX(id) last_event_id
FROM events
GROUP BY events.session_id, events.flags
ORDER BY events.id;

Make a column values header for rest of columns using TSQL

I have following table
ID | Group | Type | Product
1 Dairy Milk Fresh Milk
2 Dairy Butter Butter Cream
3 Beverage Coke Coca cola
4 Beverage Diet Dew
5 Beverage Juice Fresh Juice
I need following output/query result:
ID | Group | Type | Product
1 Dairy
1 Milk Fresh Milk
2 Butter Butter Cream
2 Beverage
1 Coke Coca cola
2 Diet Dew
3 Juice Fresh Juice
For above sample a hard coded script can do the job but I look for a dynamic script for any number of groups. I do not have any idea how it can be done so, I do not have a sample query yet. I need ideas, examples that at least give me an idea. PIVOT looks a close option but does not looks to be fully fit for this case.
Here's a possible way. It basically unions the "Group-Headers" and the "Group-Items". The difficulty was to order them correctly.
WITH CTE AS
(
SELECT ID,[Group],Type,Product,
ROW_NUMBER() OVER (PARTITION BY [Group] Order By ID)AS RN
FROM Drink
)
SELECT ID,[Group],Type,Product
FROM(
SELECT RN AS ID,[Group],[Id]AS OriginalId,'' As Type,'' As Product, 0 AS RN, 'Group' As RowType
FROM CTE WHERE RN = 1
UNION ALL
SELECT RN AS ID,'' AS [Group],[Id]AS OriginalId,Type,Product, RN, 'Item' As RowType
FROM CTE
)X
ORDER BY OriginalId ASC
, CASE WHEN RowType='Group' THEN 0 ELSE 1 END ASC
, RN ASC
Here's a demo-fiddle: http://sqlfiddle.com/#!6/ed6ca/2/0
A slightly simplified approach:
With Groups As
(
Select Distinct Min(Id) As Id, [Group], '' As [Type], '' As Product
From dbo.Source
Group By [Group]
)
Select Coalesce(Cast(Z.Id As varchar(10)),'') As Id
, Coalesce(Z.[Group],'') As [Group]
, Z.[Type], Z.Product
From (
Select Id As Sort, Id, [Group], [Type], Product
From Groups
Union All
Select G.Id, Null, Null, S.[Type], S.Product
From dbo.Source As S
Join Groups As G
On G.[Group] = S.[Group]
) As Z
Order By Sort
It should be noted that the use of Coalesce is purely for aesthetic reasons. You could simply return null in these cases.
SQL Fiddle
And an approach with ROW_NUMBER:
IF OBJECT_ID('dbo.grouprows') IS NOT NULL DROP TABLE dbo.grouprows;
CREATE TABLE dbo.grouprows(
ID INT,
Grp NVARCHAR(MAX),
Type NVARCHAR(MAX),
Product NVARCHAR(MAX)
);
INSERT INTO dbo.grouprows VALUES
(1,'Dairy','Milk','Fresh Milk'),
(2,'Dairy','Butter','Butter Cream'),
(3,'Beverage','Coke','Coca cola'),
(4,'Beverage','Diet','Dew'),
(5,'Beverage','Juice','Fresh Juice');
SELECT
CASE WHEN gg = 0 THEN dr1 END GrpId,
CASE WHEN gg = 1 THEN rn1 END TypeId,
ISNULL(Grp,'')Grp,
CASE WHEN gg = 1 THEN Type ELSE '' END Type,
CASE WHEN gg = 1 THEN Product ELSE '' END Product
FROM(
SELECT *,
DENSE_RANK()OVER(ORDER BY Grp DESC) dr1
FROM(
SELECT *,
ROW_NUMBER()OVER(PARTITION BY Grp ORDER BY type,gg) rn1,
ROW_NUMBER()OVER(ORDER BY type,gg) rn0
FROM(
SELECT Grp,Type,Product, GROUPING(Grp) gg, GROUPING(type) tg FROM dbo.grouprows
GROUP BY Product, Type, Grp
WITH ROLLUP
)X1
WHERE tg = 0
)X2
WHERE gg=1 OR rn1 = 1
)X3
ORDER BY rn0

Subtract the previous row of data where the id is the same as the row above

I have been trying all afternoon to try and achieve this with no success.
I have a db in with info on customers and the date that they purchase products from the store. It is grouped by a batch ID which I have converted into a date format.
So in my table I now have:
CustomerID|Date
1234 |2011-10-18
1234 |2011-10-22
1235 |2011-11-16
1235 |2011-11-17
What I want to achieve is to see the number of days between the most recent purchase and the last purchase and so on.
For example:
CustomerID|Date |Outcome
1234 |2011-10-18 |
1234 |2011-10-22 | 4
1235 |2011-11-16 |
1235 |2011-11-17 | 1
I have tried joining the table to itself but the problem I have is that I end up joining in the same format. I then tried with my join statement to return where it did <> match date.
Hope this makes sense, any help appreciated. I have searched all the relevant topics on here.
Will there be multiple groups of CustomerID? Or only and always grouped together?
DECLARE #myTable TABLE
(
CustomerID INT,
Date DATETIME
)
INSERT INTO #myTable
SELECT 1234, '2011-10-14' UNION ALL
SELECT 1234, '2011-10-18' UNION ALL
SELECT 1234, '2011-10-22' UNION ALL
SELECT 1234, '2011-10-26' UNION ALL
SELECT 1235, '2011-11-16' UNION ALL
SELECT 1235, '2011-11-17' UNION ALL
SELECT 1235, '2011-11-18' UNION ALL
SELECT 1235, '2011-11-19'
SELECT CustomerID,
MIN(date),
MAX(date),
DATEDIFF(day,MIN(date),MAX(date)) Outcome
FROM #myTable
GROUP BY CustomerID
SELECT a.CustomerID,
a.[Date],
ISNULL(DATEDIFF(DAY, b.[Date], a.[Date]),0) Outcome
FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY [CustomerID] ORDER BY date) Row,
CustomerID,
Date
FROM #myTable
) A
LEFT JOIN
(
SELECT ROW_NUMBER() OVER(PARTITION BY [CustomerID] ORDER BY date) Row,
CustomerID,
Date
FROM #myTable
) B ON a.CustomerID = b.CustomerID AND A.Row = B.Row + 1