Split one row into multiple rows in SQL with amounts divided equally - sql

I have a table that contains ID, an amount column and a count column. For each row I would like to split them into multiple rows, based on the count column. I would then like the amount column to be split evenly between these rows, and create a new id based on the original id and the row count.
This is how the table looks like:
ID Amount Count
1001 8 2
1002 15 3
And this is the desired output
ID Amount
1001-1 4
1001-2 4
1002-1 5
1002-2 5
1002-3 5
Whats the best approach for this?

You can use a recursive CTE. This looks something like:
with recursive cte as (
select id, amount / cnt as amount, cnt, 1 as lev
from t
union all
select id, amount, cnt, lev + 1
from t
where lev < cnt
)
select id || '-' || lev, amount
from cte;
Note that this uses standard syntax; the exact syntax might vary depending on your database.

Unfortunately, Redshift does not support recursive queries.
Here is another option using a temporary table of numbers.
create temp table tmp(n int);
insert into tmp(n) values (1), (2), (3), (4), ...; -- expand as needed
select concat(t.id, '-', p.n) id, t.amount/t.count amount
from mytable t
inner join tmp p on p.n <= t.count
order by t.id, p.n

Related

Multiply rows in single query

Table1 has the following 2 columns and 4 rows:
Entity Number
------ ------
Car 4
Shop 1
Apple 3
Pear 1
I'd like to have one set based SQL query, which produces the below desired results. Basically duplicating the Entities by the Number of times in the Number column.
I could only do it by loop through the rows one by one, which is not really elegant, neither set based.
Desired result:
Entity
------
Car
Car
Car
Car
Shop
Apple
Apple
Apple
Pear
One method uses recursive CTEs:
with cte as (
select t1.entity, t1.number
from table1 t1
union all
select cte.entity, cte.number - 1
from cte
where cte.number > 0
)
select entity
from cte;
Note: Using the default settings, this is limited to 100 rows per entity. You can use OPTION (MAXRECURSION 0) to get around this.
You can also solve this with a numbers table, but such a problem is a good introduction to recursive CTEs.
Use this
;WITH CTE
AS
(
SELECT
SeqNo = 1,
Entity,
Number
FROM YourTable
UNION ALL
SELECT
SeqNo = SeqNo+1,
Entity,
Number
FROM CTE
WHERE SeqNo < Number
)
SELECT
Entity
FROM CTE
ORDER BY 1
A non-recursion solution, will be using a fixed sequence number, then join the table based on this number like this:
WITH numbers
AS
(
SELECT n
FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9), (10)) AS numbers(n)
)
SELECT t.Entity
FROM Table1 AS t
INNER JOIN numbers as n ON t.number >= n.n;
This will support up to 10 times duplication, you can add extra numbers to support extra duplication times.
Demo
You can use spt_values as source for numbers table
select EntityList.*
from EntityList
, (
select number as n from master..spt_values WHERE Type = 'P' and Number between 1 and (select max(number) from EntityList)
) t
where n <= number
order by entity

Separating the list of value by comparing frequency

I have the bellow data set output should come like less then one frequency..
i am new to sql so don't have much idea..
in the input i have 3 times 1, 2 times 2, 3 times 3 and 2 times 4. output i want 2 times 1, 1 time 2, 2 times 3 and 1 time 4..
Any suggestion how to achieve this output!!
This can be written in a more compact form, but just for clarity:
With Src As ( --< Source table
Select * From (Values (1),(2),(3),(1),(1),(2),(3),(3),(4),(4),(5)) V (Id)
), Numbers As ( --< Auxiliary table with numbers from 1 to maximum row count of Src
Select ROW_NUMBER() Over (Order By Id) As N From Src
), Counted As ( --< Calculate current number of ID occurances
Select Id, Count(Id) As Cnt From Src Group By Id
)
Select Id
From Counted --< From distinct list of IDs
Inner Join Numbers --< replicate each row
On Numbers.N < Counted.Cnt --< one less time than the Cnt
Expression to replicate the row taken from SQL: Repeat a result row multiple times...
jpw implementation (please feel free to copy it into your own answer):
With Src As ( --< Source table
Select * From (Values (1),(2),(3),(1),(1),(2),(3),(3),(4),(4),(5)) V (Id)
), Numbered As ( --< Number ID occurances
Select Id, row_number() Over (Partition By id Order By id) As n From Src
)
Select Id From Numbered Where n > 1 --< Take one off

SQL Server 2008: Need to do math on the previous row

Working in SQL Server 2008 so the analytical functions are not an option.
Basically I have amount financed and payment made, but need to calculate interest for the first row - which is done, but need for the next row so need to grab the balance from the previous row.
Without any schema context, I can only provide a general structure, but in SQL Server 2008 you should be able to do something like this:
-- This is called a CTE (Common Table Expression)
-- Think of it as a named sub-query
;WITH computed_table AS (
-- The ROW_NUMBER() function produces an ordered computed
-- column ordered by the values in the column specified in
-- the OVER clause
SELECT ROW_NUMBER() OVER(ORDER BY Id) AS row_num
,*
FROM my_table
)
SELECT *
-- perform calculations on t1 and t2
,(t1.amount - t2.amount) AS CalculatedAmt -- example calcuation
FROM computed_table t1
OUTER APPLY (
SELECT *
FROM computed_table t2
WHERE t2.row_num = t1.row_num - 1
) AS prev
The CTE and the ROW_NUMBER() function are necessary to make sure you have a perfectly ordered column with no gaps, something which can't be guaranteed with a primary key field since rows could be deleted. The OUTER APPLY allows you to perform a table-valued operation on the individual values of the rows in the left hand table.
EDIT: To insert the results into a table, rather than just selecting them, you can add a INSERT clause after the SELECT clause:
...(CTE HERE)...
SELECT *
-- perform calculations on t1 and t2
,(t1.amount - t2.amount) AS CalculatedAmt -- example calcuation
-- This INSERT clause will insert the result set into my_table. Make
-- sure the column aliases in the SELECT clause match the column names
-- in my_table.
INTO my_table
FROM computed_table t1
...(REST OF QUERY HERE)...
Try this example
DECLARE #tbl TABLE(ID INT, Test VARCHAR(100),SortKey INT);
INSERT INTO #tbl VALUES(1,'Test 1 3',3),(2,'Test 2 4',4),(3,'Test 3 1',1),(4,'Test 4 2',2);
WITH Sorted AS
(
SELECT ROW_NUMBER() OVER(ORDER BY SortKey) AS Nr
,*
FROM #tbl
)
SELECT s.Test
,(SELECT prev.Test FROM Sorted AS prev WHERE s.Nr=prev.Nr+1) AS PreviousRow
,(SELECT nxt.Test FROM Sorted AS nxt WHERE s.Nr=nxt.Nr-1) AS NextRow
FROM sorted AS s
Attention
ROW_NUMBER() OVER() will only work as expected, if the values you are sorting after are unique!
The result
Test PreviousRow NextRow
Test 3 1 NULL Test 4 2
Test 4 2 Test 3 1 Test 1 3
Test 1 3 Test 4 2 Test 2 4
Test 2 4 Test 1 3 NULL

SQL select segment

I'm using SQL Server 2008.
I have a table with x amount of rows. I would like to always divide x by 5 and select the 3rd group of records.
Let's say there are 100 records in the table:
100 / 5 = 20
the 3rd segment will be record 41 to 60.
How will I be able in SQL to calculate and select this 3rd segment only?
Thanks.
You can use NTILE.
Distributes the rows in an ordered partition into a specified number of groups.
Example:
SELECT col1, col2, ..., coln
FROM
(
SELECT
col1, col2, ..., coln,
NTILE(5) OVER (ORDER BY id) AS groupno
FROM yourtable
)
WHERE groupno = 3
That's a perfect use for the NTILE ranking function.
Basically, you define your query inside a CTE and add an NTILE to your rows - a number going from 1 to n (the argument to NTILE). You order your rows by some column, and then you get the n groups of rows you're looking for, and you can operate on any one of those "groups" of data.
So try something like this:
;WITH SegmentedData AS
(
SELECT
(list of your columns),
GroupNo = NTILE(5) OVER (ORDER BY SomeColumnOfYours)
FROM dbo.YourTable
)
SELECT *
FROM SegmentedData
WHERE GroupNo = 3
Of course, you can also use an UPDATE statement after the CTE to update those rows.

Get row count including column values in sql server

I need to get the row count of a query, and also get the query's columns in one single query. The count should be a part of the result's columns (It should be the same for all rows, since it's the total).
for example, if I do this:
select count(1) from table
I can have the total number of rows.
If I do this:
select a,b,c from table
I'll get the column's values for the query.
What I need is to get the count and the columns values in one query, with a very effective way.
For example:
select Count(1), a,b,c from table
with no group by, since I want the total.
The only way I've found is to do a temp table (using variables), insert the query's result, then count, then returning the join of both. But if the result gets thousands of records, that wouldn't be very efficient.
Any ideas?
#Jim H is almost right, but chooses the wrong ranking function:
create table #T (ID int)
insert into #T (ID)
select 1 union all
select 2 union all
select 3
select ID,COUNT(*) OVER (PARTITION BY 1) as RowCnt from #T
drop table #T
Results:
ID RowCnt
1 3
2 3
3 3
Partitioning by a constant makes it count over the whole resultset.
Using CROSS JOIN:
SELECT a.*, b.numRows
FROM YOUR_TABLE a
CROSS JOIN (SELECT COUNT(*) AS numRows
FROM YOUR_TABLE) b
Look at the Ranking functions of SQL Server.
SELECT ROW_NUMBER() OVER (ORDER BY a) AS 'RowNumber', a, b, c
FROM table;
You could do it like this:
SELECT x.total, a, b, c
FROM
table
JOIN (SELECT total = COUNT(*) FROM table) AS x ON 1=1
which will return the total number of records in the first column, followed by fields a,b & c