select query to get result by merging two columns - sql

I have a table like below :
Id Price1 Price2
3 30 20
3 40 20
3 50 20
I want to write a query to get a below result :
Desired Ouput :
RowNo Id Price
1 3 20
2 3 30
3 3 40
4 3 50
Please help!!

Use Cross apply to unpivot the data and generate row number
SELECT Row_number()OVER(ORDER BY price) AS Rowno,*
FROM (SELECT DISTINCT Id,
Price
FROM (VALUES (3,30,20),
(3,40,20),
(3,50,20) ) tc ( Id, Price1, Price2)
CROSS apply (VALUES (Price1),
(Price2)) Cs (Price)) A
Result :
╔═══════╦════╦═══════╗
║ Rowno ║ Id ║ Price ║
╠═══════╬════╬═══════╣
║ 1 ║ 3 ║ 20 ║
║ 2 ║ 3 ║ 30 ║
║ 3 ║ 3 ║ 40 ║
║ 4 ║ 3 ║ 50 ║
╚═══════╩════╩═══════╝

You can use union to combine the rows (so duplicates are removed). And then row_number() to calculate rownum:
select row_number() over (order by price) as rownum, id, price
from ((select id, price1 as price from t) union
(select id, price2 from t
) t
order by price;

Related

Group chronologically

I'm trying to get the top result from each status from a table grouped by customer id and status, ordered by time.
The data given:
CustNo Date Status
1 2016-03-24 C
1 2016-02-08 C
1 2016-01-17 A
1 2015-12-04 C
2 2016-04-28 B
2 2016-03-25 C
2 2016-02-13 C
2 2016-01-04 C
3 2016-02-02 A
3 2016-01-09 A
3 2015-12-12 A
3 2015-11-30 A
I want the output to look like this:
CustNo Date Status
1 2016-03-24 C
1 2016-01-17 A
1 2015-12-04 C
2 2016-04-28 B
2 2016-03-25 C
3 2016-02-02 A
As you can see I want the top date for each status change (if any) within each customer. I solved it for customer 2 and 3 where there is no change of status or the status never changes back, but as for customer 1 the status has changed from C to A and back to C and this is the tricky part (for me at least). I always seem to get the C status grouped all together.
You can try this.
With CTE as
{
select CustNo,
DATE,
Status,
ROW_NUMBER () over (partition by CustNo order by date) as ord
from tab
)
SELECT CustNo,
DATE,
Status
FROM CTE
EXCEPT
SELECT C1.CustNo,
C1.DATE,
C1.Status
FROM CTE C1
INNER JOIN CTE C2 ON C1.CustNo = C2.CustNo
AND C1.Status = C2.Status
AND C1.ord + 1 = C2.ord
You could use windowed function ROW_NUMBER to calculate groups:
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY CustNo ORDER BY Date) -
ROW_NUMBER() OVER(PARTITION BY CustNo, Status ORDER BY Date) AS grp
FROM mytable
)
SELECT CustNo, Status, MAX(Date) AS Date
FROM cte
GROUP BY CustNo, Status, grp
ORDER BY CustNo, Date DESC;
LiveDemo
Output:
╔════════╦════════╦═════════════════════╗
║ CustNo ║ Status ║ Date ║
╠════════╬════════╬═════════════════════╣
║ 1 ║ C ║ 24.03.2016 00:00:00 ║
║ 1 ║ A ║ 17.01.2016 00:00:00 ║
║ 1 ║ C ║ 04.12.2015 00:00:00 ║
║ 2 ║ B ║ 28.04.2016 00:00:00 ║
║ 2 ║ C ║ 25.03.2016 00:00:00 ║
║ 3 ║ A ║ 02.02.2016 00:00:00 ║
╚════════╩════════╩═════════════════════╝

sql sorting by subgroup sum data

How sort this
a 1 15
a 2 3
a 3 34
b 1 55
b 2 44
b 3 8
to (by third column sum):
b 1 55
b 2 44
b 3 8
a 1 15
a 2 3
a 3 34
since (55+44+8) > (15+3+34)
If you are using SQL Server/Oracle/Postgresql you could use windowed SUM:
SELECT *
FROM tab
ORDER BY SUM(col3) OVER(PARTITION BY col) DESC, col2
LiveDemo
Output:
╔═════╦══════╦══════╗
║ col ║ col2 ║ col3 ║
╠═════╬══════╬══════╣
║ b ║ 1 ║ 55 ║
║ b ║ 2 ║ 44 ║
║ b ║ 3 ║ 8 ║
║ a ║ 1 ║ 15 ║
║ a ║ 2 ║ 3 ║
║ a ║ 3 ║ 34 ║
╚═════╩══════╩══════╝
You can do this using ANSI standard window functions. I prefer to use a subquery although this is not strictly necessary:
select col1, col2, col3
from (select t.*, sum(col3) over (partition by col1) as sumcol3
from t
) t
order by sumcol3 desc, col3 desc;
...and an example how to do it without windowing functions, in for example MySQL (but also in just about any other standard SQL version)
SELECT m.col1, m.col2, m.col3
FROM myTable m
JOIN (
SELECT col1, SUM(col3) groupsum FROM myTable GROUP BY col1
) z ON m.col1 = z.col1
ORDER BY z.groupsum DESC, col2;
Basically, calculate the group sum in a subquery and join/order the results by the group's sum descending.
An SQLfiddle to test with.

SQL Add Running Total Column for each item in a giant table

I wanna calculate a running total based on 5 types of transactions (let's say transaction A, B, C, D, E). But I have over one thousand different products in this table and each product could have millions of transaction records on different days.
So the table looks like this:
ProductID A B C D E Running Total
1 10 0 5 0 5 20
2 15 0 0 0 0 15
3 20 5 0 10 0 35
1 10 0 0 0 0 30 (20 for product 1, plus 10 for product 1 again)
3 12 0 33 0 0 80 (35 for product 3, plus 45 for product 3 again)
The ANSI standard method is to use sum() as a window function:
select t.*,
sum(a + b + c + d + e) over (partition by productid order by <datetimecol>) as RunningTotal
from table t;
SQL tables represent unordered sets, so you need a column that specifies the ordering. I am guessing there is a date/time column somewhere for this purpose.
Most databases support this standard syntax: Oracle, SQL Server 2012+, Postgres, Teradata, and DB2.
Test Data
DECLARE #TABLE TABLE (ProductID INT, A INT, B INT, C INT, D INT, E INT)
INSERT INTO #TABLE VALUES
(1 ,10, 0, 5 , 0 , 5), -- 20
(2 ,15, 0, 0 , 0 , 0), -- 15
(3 ,20, 5, 0 , 10, 0), -- 35
(1 ,10, 0, 0 , 0 , 0), -- 30 (20 for product 1, plus 10 for product 1 again)
(3 ,12, 0, 33, 0 , 0) -- 80
Query
;WITH CTE AS
(
select *
,ROW_NUMBER() OVER (PARTITION BY ProductID ORDER BY ProductID ASC) rn
from #TABLE
)
SELECT ProductID
,A
,B
,C
,D
,E
,runningTotal
FROM CTE c
cross apply (select sum(A+B+C+D+E) as runningTotal
from CTE
where rn <= c.rn
and ProductID = c.ProductID
) as rt
Result
╔═══════════╦════╦═══╦════╦════╦═══╦══════════════╗
║ ProductID ║ A ║ B ║ C ║ D ║ E ║ runningTotal ║
╠═══════════╬════╬═══╬════╬════╬═══╬══════════════╣
║ 1 ║ 10 ║ 0 ║ 5 ║ 0 ║ 5 ║ 20 ║
║ 1 ║ 10 ║ 0 ║ 0 ║ 0 ║ 0 ║ 30 ║
║ 2 ║ 15 ║ 0 ║ 0 ║ 0 ║ 0 ║ 15 ║
║ 3 ║ 20 ║ 5 ║ 0 ║ 10 ║ 0 ║ 35 ║
║ 3 ║ 12 ║ 0 ║ 33 ║ 0 ║ 0 ║ 80 ║
╚═══════════╩════╩═══╩════╩════╩═══╩══════════════╝

How to sort a column based on length of data in it in SQL server

As we all know general sorting is using order by. The sort I want to perform is different. I want the smallest length value in middle of table n the largest ones in top and bottom of it. One half should be descending and another half should be ascending. Can you guys help. It was an interview question.
This is one way:
;WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER(ORDER BY LEN(YourColumn))
FROM dbo.YourTable
)
SELECT *
FROM CTE
ORDER BY RN%2, (CASE WHEN RN%2 = 0 THEN 1 ELSE -1 END)*RN DESC
Test Data
DECLARE #Table TABLE
(ID INT, Value VARCHAR(10))
INSERT INTO #Table VALUES
(1 , 'A'),
(2 , 'AB'),
(3 , 'ABC'),
(4 , 'ABCD'),
(5 , 'ABCDE'),
(6 , 'ABCDEF'),
(7 , 'ABCDEFG'),
(8 , 'ABCDEFGI'),
(9 , 'ABCDEFGIJ'),
(10 ,'ABCDEFGIJK')
Query
;WITH CTE AS (
SELECT *
,NTILE(2) OVER (ORDER BY LEN(Value) DESC) rn
FROM #Table )
SELECT *
FROM CTE
ORDER BY CASE WHEN rn = 1 THEN LEN(Value) END DESC
,CASE WHEN rn = 2 THEN LEN(Value) END ASC
Result
╔════╦════════════╦════╗
║ ID ║ Value ║ rn ║
╠════╬════════════╬════╣
║ 10 ║ ABCDEFGIJK ║ 1 ║
║ 9 ║ ABCDEFGIJ ║ 1 ║
║ 8 ║ ABCDEFGI ║ 1 ║
║ 7 ║ ABCDEFG ║ 1 ║
║ 6 ║ ABCDEF ║ 1 ║
║ 1 ║ A ║ 2 ║
║ 2 ║ AB ║ 2 ║
║ 3 ║ ABC ║ 2 ║
║ 4 ║ ABCD ║ 2 ║
║ 5 ║ ABCDE ║ 2 ║
╚════╩════════════╩════╝
Here's a short approach that would ge t you started:
WITH cte AS
(
SELECT TOP 1000 number
FROM master..spt_values
WHERE type = 'P' and number >0
)
SELECT number, row_number() OVER(ORDER BY CASE WHEN number %2 = 1 THEN number ELSE -(number) END) pos
FROM cte

Update duplicate fields in table

I have table with about 100000 records.I need update same fields like this.
For example this is my table
id name
1 sss
2 bbb
3 ccc
4 avg
5 bbb
6 bbb
7 sss
8 mmm
9 avg
After executing script I need get
id name
1 sss
2 bbb
3 ccc
4 avg
5 bbb-5
6 bbb-6
7 sss-7
8 mmm
9 avg-9
How can I do that?
By using CTE
WITH greaterRecord
AS
(
SELECT id,
name,
ROW_NUMBER() OVER(PARTITION BY name ORDER BY id) RN
FROM TableName
)
UPDATE greaterRecord
SET name = name + '-' + CAST(id AS VARCHAR(10))
WHERE RN > 1
SQLFiddle Demo
This is the common query that works on most RDBMS
UPDATE a
SET a.Name = a.Name + '-' + CAST(ID AS VARCHAR(10))
FROM tableName a
LEFT JOIN
(
SELECT MIN(ID) min_ID, name
FROM tableName
GROUP BY name
) b ON a.name = b.name AND
a.ID = b.Min_ID
WHERE b.Name IS NULL
SQLFiddle Demo
OUTPUT after the update statement has been executed
╔════╦═══════╗
║ ID ║ NAME ║
╠════╬═══════╣
║ 1 ║ sss ║
║ 2 ║ bbb ║
║ 3 ║ ccc ║
║ 4 ║ avg ║
║ 5 ║ bbb-5 ║
║ 6 ║ bbb-6 ║
║ 7 ║ sss-7 ║
║ 8 ║ mmm ║
║ 9 ║ avg-9 ║
╚════╩═══════╝
This should do:
;WITH CTE AS
(
SELECT id,
name,
RN = ROW_NUMBER() OVER(PARTITION BY name ORDER BY id)
FROM YourTable
)
UPDATE CTE
SET name = name + '-' + CAST(id AS VARCHAR(8))
WHERE RN > 1