SQL Group up rows data into one row - sql

I have data in a table like this:
I want to organise the table data so that I can get a maximum of 3 letters per row grouped by account number.
Below would be the result I want:
I can use dense rank to group up the account numbers but not sure how to get the data I want in the format above.
Logic:
There are 4 letters for account 123. Final result groups by account number with first 3 letters as you can only have a maximum of 3 letters per row. The fourth letter must go on the second row.

Here's one option using conditional aggregation, first creating a row_number, and then creating a row grouping using every 3 rows with % (modulus operator):
select account_number,
max(case when rn % 3 = 1 then letter end) as letter1,
max(case when rn % 3 = 2 then letter end) as letter2,
max(case when rn % 3 = 0 then letter end) as letter3
from (
select *, row_number() over (partition by account_number, rn % 3 order by rn) newrn
from (
select *, row_number() over (partition by account_number order by letter) rn
from yourtable
) t
) y
group by account_number, newrn
order by account_number
Online Demo

I would do this with only one call to row_number():
select account_number,
max(case when seqnum % 3 = 1 then letter end) as letter_a,
max(case when seqnum % 3 = 2 then letter end) as letter_b,
max(case when seqnum % 3 = 0 then letter end) as letter_c
from (select t.*,
row_number() over (partition by account_number order by letter) as seqnum
from t
) t
group by account_number, floor( (seqnum - 1) / 3)
order by account_number, min(seqnum);

Related

SUM a specific column in next rows until a condition is true

Here is a table of articles and I want to store sum of Mass Column from next rows in sumNext Column based on a condition.
If next row has same floor (in floorNo column) as current row, then add the mass of next rows until the floor is changed
E.g : Rows three has sumNext = 2. That is computed by adding the mass from row four and row five because both rows has same floor number as row three.
id
mass
symbol
floorNo
sumNext
2891176
1
D
1
0
2891177
1
L
8
0
2891178
1
L
1
2
2891179
1
L
1
1
2891180
1
1
0
2891181
1
5
2
2891182
1
5
1
2891183
1
5
0
Here is the query, that is generating this table, I just want to add sumNext column with the right value inside.
WITH items AS (SELECT
SP.id,
SP.mass,
SP.symbol,
SP.floorNo
FROM articles SP
ORDER BY
DECODE(SP.symbol,
'P',1,
'D',2,
'L',3,
4 ) asc)
SELECT CLS.*
FROM items CLS;
You could use below solution which uses
common table expression (cte) technique to put all consecutive rows with same FLOORNO value in the same group (new grp column).
Then uses the analytic version of SUM function to sum all next MASS per grp column as required.
Items_RowsNumbered (id, mass, symbol, floorNo, rnb) as (
select ID, MASS, SYMBOL, FLOORNO
, row_number()over(
order by DECODE(symbol, 'P',1, 'D',2, 'L',3, 4 ) asc, ID )
/*
You need to add ID column (or any others columns that can identify each row uniquely)
in the "order by" clause to make the result deterministic
*/
from (Your source query)Items
)
, cte(id, mass, symbol, floorNo, rnb, grp) as (
select id, mass, symbol, floorNo, rnb, 1 grp
from Items_RowsNumbered
where rnb = 1
union all
select t.id, t.mass, t.symbol, t.floorNo, t.rnb
, case when t.floorNo = c.floorNo then c.grp else c.grp + 1 end grp
from Items_RowsNumbered t
join cte c on (c.rnb + 1 = t.rnb)
)
select
ID, MASS, SYMBOL, FLOORNO
/*, RNB, GRP*/
, nvl(
sum(MASS)over(
partition by grp
order by rnb
ROWS BETWEEN 1 FOLLOWING and UNBOUNDED FOLLOWING)
, 0
) sumNext
from cte
;
demo on db<>fiddle
This is a typical gaps-and-islands problem. You can use LAG() in order to determine the exact partitions, and then SUM() analytic function such as
WITH ii AS
(
SELECT i.*,
ROW_NUMBER() OVER (ORDER BY id DESC) AS rn2,
ROW_NUMBER() OVER (PARTITION BY floorNo ORDER BY id DESC) AS rn1
FROM items i
)
SELECT id,mass,symbol, floorNo,
SUM(mass) OVER (PARTITION BY rn2-rn1 ORDER BY id DESC)-1 AS sumNext
FROM ii
ORDER BY id
Demo

(HANA SQL) Show multiple values in one row

I am trying to complete the following:
Old situation
What I want
For a fixed maximum number of target columns, you can use window functions and conditional aggregation:
select customer,
max(case when rn = 1 then order_date end) as order_date_1,
max(case when rn = 2 then order_date end) as order_date_2,
max(case when rn = 3 then order_date end) as order_date_3
from (
select t.*, row_number() over(partition by customer order by order_date) rn
from mytable t
) t
group by customer

sql table random numbers into multiple columns

new here - but would appreciate any help
I have a table with one column - this column has numbers from 1 to 1000
I would like to break this column up into ten columns - so I would have 10 columns and 100 rows in my new table
I would also like the numbers to be in random order
Any help would really be appreciated -- thanks in advance
You can use conditional aggregation and row_number():
select max(case when seqnum % 10 = 0 then number end) as number_1,
max(case when seqnum % 10 = 1 then number end) as number_2,
. . .
max(case when seqnum % 10 = 9 then number end) as number_10
from (select t.*,
row_number() over (order by newid()) - 1 as seqnum
from t
) t
group by floor(seqnum / 10)

Calculate lead-time between selected rows (SQL)

Given this table where we have users, the product that they used, and the first date that they used the product (I have also created a simple rank by user window). Note, each user will only have minimum 0 rows if they used nothing before, and 2 rows, if they used both products. There are only 3 products - cigars and beers.
How can I create a new view where each row is 1 user, the next column shows the first product, the next column shows the 2nd product, and the last column shows the lead-time b/w the first dates of use?
One method is conditional aggregation with row_number():
select user,
max(case when seqnum = 1 then product end) as product_1,
max(case when seqnum = 2 then product end) as product_2,
(max(case when seqnum = 2 then time_used end) -
max(case when seqnum = 1 then time_used end)
) as dif
from (select t.*,
row_number() over (partition by user order by time_used) as seqnum
from t
) t
group by user;
Date/time functions vary significantly across different databases. Not all support a simple -, so you might nee to adjust for your database.
Minus between dates may not work on each database
select
c1.user_id,
c1.first_product_used,
c2.second_product_used,
COALESCE(CAST((Cast(c2.second_date AS DATE) - Cast(c1.first_date AS DATE)) AS VARCHAR(20)), 'n/a') AS "leadtime_days"
from
(
select
user_id,
product_used AS first_product_used,
time_used AS first_date
from
check2
where
rank_of_use = 1
)c1
LEFT OUTER JOIN
(
select
user_id,
product_used AS second_product_used,
time_used AS second_date
from
check2
where
rank_of_use = 2
)c2
ON
c1.user_id = c2.user_id

Split Record into multiple column after Row_number and Partition By

I want to have my result to be displayed in 4 columns.
This is how I did my coding.
SELECT T.MT_CARD_TYP_ID,
ROW_NUMBER() OVER (PARTITION BY T.APP_ID ORDER BY T.DT_CREATE) AS RN
FROM T_CS_FAC_CC T
WHERE app_id = '8F9A97B0CB5349429C44F15830EDC18F';
What should I do the next step? Can someone pro help me out please?
This is how my result look
This is how I want the result look like.
You can try to use condition aggravated function.
SELECT app_id,
MAX(CASE WHEN RN = 1 THEN MT_CARD_TYP_ID END),
MAX(CASE WHEN RN = 3 THEN MT_CARD_TYP_ID END),
MAX(CASE WHEN RN = 2 THEN MT_CARD_TYP_ID END),
MAX(CASE WHEN RN = 4 THEN MT_CARD_TYP_ID END)
FROM (
SELECT T.MT_CARD_TYP_ID, ROW_NUMBER() OVER(PARTITION BY T.APP_ID ORDER BY T.DT_CREATE) AS RN
FROM T_CS_FAC_CC T
where app_id='8F9A97B0CB5349429C44F15830EDC18F'
)t1
GROUP BY app_id