Convert sql output to following format - sql

I want to convert sql out put to following format.
Here is my table.
Id Country Code Totalcount
1 India 20 120
2 India 21 121
3 India 22 122
4 India 23 123
5 India 24 124
6 US 20 220
7 US 21 221
8 Us 22 222
9 UK 23 323
10 UK 24 324
Select Country, 20,21,22,23,24,25
from
(
Select Country,StatusCode,Totalcount from StatusDetails
) as SourceTable
Pivot
(
sum(Totalcount) for StatusCode in (20,21,22,23,24,25)
) as PivotTable
In Need Output like below one.Do I need to apply pivot table.
Country 20 21 22 23 24
India 120 121 122 123 124
US 220 221 222
UK 323 324

I am a fan of conditional aggregation for this purpose:
select country,
max(case when code = 20 then totalcount end) as cnt_20,
max(case when code = 21 then totalcount end) as cnt_21,
max(case when code = 22 then totalcount end) as cnt_22,
max(case when code = 23 then totalcount end) as cnt_23,
max(case when code = 24 then totalcount end) as cnt_24
from sourcetable
group by country

Yes you will need pivot & your code would also work along with quote's :
select pt.*
from (select Country, Code, Totalcount
from sourcetable
)as SourceTable Pivot
(sum(Totalcount) for Code in ([20],[21],[22],[23],[24],[25])
)as pt;

Related

Grouping like records

We have a set of data where there are 2 records for each CODE. An example of the data is this:
TICKER CODE SCORE PRICE PCF
---------------------------------------
ABC 23 A 100 20
DEF 23 B 200 30
XXX 52 C 300 40
YYY 52 D 400 50
GHI 86 E 500 60
JKL 86 F 600 70
MNO 27 G 700 80
PQR 27 H 800 90
So, what we need to do is create a query which will return the columns of the like records by CODE into 1 record like this:
CODE,TICKER_1,SCORE_1,PRICE_1,PCF_1,TICKER_2,SCORE_2,PRICE_2,PCF_2
23,ABC,A,100,20,DEF,B,200,30
52,XXX,C,300,40,YYY,D,400,40
86,GHI,E,500,60,JKL,F,600,70
27,MNO,G,700,80,PQR,H,800,90
So, that they are combined by like CODE values.
You may try the following which assigns uses ROW_NUMBER to assign a row number for each code entry before using MAX with a case expression to filter for each entry.
Eg.
SELECT
CODE,
MAX(CASE WHEN rn=1 THEN TICKER END) AS TICKER_1,
MAX(CASE WHEN rn=1 THEN SCORE END) AS SCORE_1,
MAX(CASE WHEN rn=1 THEN PRICE END) AS PRICE_1,
MAX(CASE WHEN rn=1 THEN PCF END) AS PCF_1,
MAX(CASE WHEN rn=2 THEN TICKER END) AS TICKER_2,
MAX(CASE WHEN rn=2 THEN SCORE END) AS SCORE_2,
MAX(CASE WHEN rn=2 THEN PRICE END) AS PRICE_2,
MAX(CASE WHEN rn=2 THEN PCF END) AS PCF_2
FROM (
SELECT
m.*,
ROW_NUMBER() OVER (PARTITION BY CODE ORDER BY TICKER) as rn
FROM mytable m
) m1
GROUP BY
CODE
Outputs:
CODE
TICKER_1
SCORE_1
PRICE_1
PCF_1
TICKER_2
SCORE_2
PRICE_2
PCF_2
23
ABC
A
100
20
DEF
B
200
30
27
MNO
G
700
80
PQR
H
800
90
52
XXX
C
300
40
YYY
D
400
50
86
GHI
E
500
60
JKL
F
600
70
For debugging purposes, the output of the subquery
SELECT
m.*,
ROW_NUMBER() OVER (PARTITION BY CODE ORDER BY TICKER) as rn
FROM mytable m
looks like this:
TICKER
CODE
SCORE
PRICE
PCF
RN
ABC
23
A
100
20
1
DEF
23
B
200
30
2
MNO
27
G
700
80
1
PQR
27
H
800
90
2
XXX
52
C
300
40
1
YYY
52
D
400
50
2
GHI
86
E
500
60
1
JKL
86
F
600
70
2
View working demo on db fiddle
Notable alternatives
Instead of CASE WHEN rn=1 THEN TICKER END you could also use the DECODE function available in oracle as DECODE(rn,1,TICKER)
You may also use a pivot as shown below (NB. Column names are not as in the expected result)
WITH cte as (
SELECT
m.*,
ROW_NUMBER() OVER (PARTITION BY CODE ORDER BY TICKER) as rn
FROM mytable m
)
SELECT * FROM cte
PIVOT (
MAX(TICKER) as "TICKER",
MAX(SCORE) as "SCORE",
MAX(PRICE) as "PRICE",
MAX(PCF) as "PCF"
FOR rn IN (1,2)
)
Outputs:
CODE
1_TICKER
1_SCORE
1_PRICE
1_PCF
2_TICKER
2_SCORE
2_PRICE
2_PCF
23
ABC
A
100
20
DEF
B
200
30
27
MNO
G
700
80
PQR
H
800
90
52
XXX
C
300
40
YYY
D
400
50
86
GHI
E
500
60
JKL
F
600
70
View working demo on db fiddle here

SQL Grouping and dense rank concept

I have a data set that looks like:
cust city hotel_id amount
-------------------------------
A 1 252 3160
B 1 256 1893
C 2 105 2188
D 2 105 3054
E 3 370 6107
F 2 110 3160
G 2 150 1893
H 3 310 2188
I 1 252 3160
J 1 250 4000
K 3 370 5000
L 3 311 1095
Query to display the top 3 hotels by revenue (Sum of amount) for each city?
Since same hotel can be booked by other customer in same city so we need to sum the amount to find total amount.
Expected output:
city hotel_id amount
---------------------------
1 252 6320
1 250 4000
1 256 1893
2 105 5242
2 110 3160
2 150 1893
3 370 11107
3 310 2188
3 311 1095
SELECT
t.city, t.hotel_id, t.amount
FROM
(
SELECT city, hotel_id, SUM(amount) AS amount,
ROW_NUMBER() OVER (PARTITION BY city ORDER BY SUM(amount) DESC) AS rn
FROM yourTable
GROUP BY city, hotel_id
) t
WHERE t.rn <= 3
ORDER BY t.city, t.amount DESC;
Demo here:
Rextester
To get the total sum for each hotel_id you need to group by that column first, then group by the city for syntax purposes. The #tmp table here should have all of the data you need, so then you just have to select the top 3 entries for each city from there.
SELECT city, hotel_id, SUM(amount) AS 'total' INTO #tmp
FROM [table]
GROUP BY hotel_id, city
(SELECT TOP 3 *
FROM #tmp
WHERE city = 1)
UNION
(SELECT TOP 3 *
FROM #tmp
WHERE city = 2)
UNION
(SELECT TOP 3 *
FROM #tmp
WHERE city = 3)

sql query to get for each item the line of a group that has the maximum occurence

the question is difficult to summarize in the title, so here a more verbose example:
I have a huge dataset of dozens of measurements for thousands of different objects. Most of them have an associated type but this type is not unambiguous.
So a Select like
SELECT oid, type, count(type) FROM data GROUP BY oid, type;
will produce something like:
oid type count(type)
0 0 22
1 0 22
2 1 61
2 2 104
3 2 63
4 0 34
6 0 1
8 2 76
9 0 1
11 3 33
12 0 55
13 4 1
13 5 28
13 1 2
13 2 255
14 4 148
14 1 4
14 2 3
15 3 10
16 0 13
18 4 137
18 1 5
How can i get only one line per object to the result if this only line has to be the one with the most occurences?
Bonus-Question: also get a percentage per object line that represents the occurrence ratio of this type.
The result should look like:
oid type P(type)
0 0 1.0
1 0 1.0
2 2 0.64
3 2 1.0
4 0 1.0
6 0 1.0
8 2 1.0
9 0 1.0
11 3 1.0
12 0 1.0
13 2 0.89
14 4 0.95
15 3 1.0
16 0 1.0
18 4 0.96
edit:
some test data and the almost-correct output of one solution:
http://pastebin.com/jVvHErJ2
This query solves both your problems
SELECT s.oid,
s.type,
s.total_per_oid_per_type,
(s.total_per_oid_per_type + 0.0) / s.total_per_oid AS percentage
FROM (SELECT v.oid,
v.type,
v.total_per_oid_per_type,
ROW_NUMBER() OVER (PARTITION BY v.oid ORDER BY v.total_per_oid_per_type DESC) AS object_number,
SUM(v.total_per_oid_per_type) OVER (PARTITION BY v.oid) AS total_per_oid
FROM (SELECT t.oid, t.type, count(1) AS total_per_oid_per_type
FROM data t
GROUP BY t.oid, t.type) v ) s
WHERE object_number = 1
Solution special for Sqlite3 (equals to above)
WITH v AS (
SELECT oid,
type,
COUNT(1) AS total_per_oid_per_type
FROM data
GROUP BY oid, type
),
s AS (
SELECT oid,
MAX(total_per_oid_per_type) AS max_total_per_oid
FROM v
GROUP BY oid
),
totals AS (
SELECT oid,
SUM(total_per_oid_per_type) AS total_per_oid
FROM v
GROUP BY oid
)
SELECT v.oid,
v.type,
v.total_per_oid_per_type,
(v.total_per_oid_per_type + 0.0) / totals.total_per_oid AS percentage
FROM v
INNER JOIN s ON v.oid = s.oid AND v.total_per_oid_per_type = s.max_total_per_oid
INNER JOIN totals ON v.oid = totals.oid
ORDER BY v.oid, v.type
Try this it should work
create table ##TBL (oid INT, [type] INT, [count(type)] INT)
INSERT INTO ##TBL VALUES
(0,0,22),
(1,0,22),
(2,1,61),
(2,2,104),
(3,2,63),
(4,0,34),
(6,0,1),
(8,2,76),
(9,0,1),
(11,3,33),
(12,0,55),
(13,4,1),
(13,5,28),
(13,1,2),
(13,2,255),
(14,4,148),
(14,1,4),
(14,2,3),
(15,3,10),
(16,0,13),
(18,4,137),
(18,1,5)
--------------------------------
SELECT oid
,max([type]) as x
--,Max([count(type)]) AS [count(type)]
,CAST( CAST( MAX([count(type)]) AS DECIMAL(10,2) ) / CAST( SUM([count(type)]) AS DECIMAL(10,2) ) AS DECIMAL(10,2) ) AS 'Percent %'
from ##TBL
group by oid

Dynamically pivot to fixed number of columns

This is the structure of my data
Name TransID Amount
Joe 123 56
Joe 124 55
Joe 125 58
Tom 126 31
Tom 127 48
I have a requirement to report from this data in the below format
Name Amount1 Amount2
Joe 56 55
Joe 58
Tom 31 48
Joe has three Amounts in the original data set but I need a fixed number of columns (two) in the view. Therefore, the third Amount for Joe is inserted as a new record in the view. Is it possible to achieve this as a stored procedure or creating a view.
Break the problems into smaller steps. These are the steps that I would take:
Use ROW_NUMBER() OVER (PARTITION BY ...):
Name TransID Amount Row_Number
Joe 123 56 1
Joe 124 55 2
Joe 125 58 3
Tom 126 31 1
Tom 127 48 2
Subtract 1.
Name TransID Amount RowNumberStartingWith0
Joe 123 56 0
Joe 124 55 1
Joe 125 58 2
Tom 126 31 0
Tom 127 48 1
Divide it by 2, get the result of the division and the remainder modulo 2:
Name TransID Amount Result Remainder
Joe 123 56 0 0
Joe 124 55 0 1
Joe 125 58 1 0
Tom 126 31 0 0
Tom 127 48 0 1
Drop the TransID column. The remainder is always 0 or 1, so you can pivot on it:
Name Result AmountForRemainder0 AmountForRemainder1
Joe 0 56 55
Joe 1 58
Tom 0 31 48
Now you drop the Result column and rename your columns:
Name Amount1 Amount2
Joe 56 55
Joe 58
Tom 31 48
Profit.
TRY this and let me know .Check with other sample data also.I am getting the desire output.
DECLARE #t TABLE (
NAME VARCHAR(50)
,TransID INT
,Amount INT
)
INSERT INTO #t
VALUES ('Joe',123,56)
,('Joe',124,55)
,('Joe',125,58)
,('Tom',126,31)
,('Tom',127,48)
,('Tom',128,89)
,('Tom',129,90)
,('Joe',130,68);
WITH CTE
AS (
SELECT *
,row_number() OVER (
PARTITION BY NAME ORDER BY amount
) rn
FROM #t
)
,CTE1
AS (
SELECT NAME
,(
SELECT amount
FROM cte
WHERE rn = 1
AND NAME = a.NAME
) [Amount1]
,(
SELECT amount
FROM cte
WHERE rn = 2
AND NAME = a.NAME
) [Amount2]
,rn
FROM cte A
WHERE rn = 1
UNION ALL
SELECT b.NAME
,a.amount
,isnull(c.amount, 0)
,a.rn
FROM CTE1 B
INNER JOIN CTE A ON a.NAME = b.NAME
AND a.rn % 2 <> 0
AND a.rn > 1
AND b.rn <> a.rn
OUTER APPLY (
SELECT Amount
FROM CTE C
WHERE NAME = b.NAME
AND rn % 2 = 0
AND rn > 2
) c
)
SELECT *
FROM cte1

SQL - Number of entries needed to reach given value

I need to find how many records it took to reach a given value. I have a table in the below format:
ID Name Time Time 2
1 Campaign 1 7 100
2 Campaign 3 5 165
3 Campaign 1 3 321
4 Campaign 2 610 952
5 Campaign 2 15 13
6 Campaign 2 310 5
7 Campaign 3 0 3
8 Campaign 1 0 610
9 Campaign 1 1 15
10 Campaign 1 54 310
11 Campaign 3 4 0
12 Campaign 2 23 0
13 Campaign 2 8 1
14 Campaign 3 23 1
15 Campaign 3 7 0
16 Campaign 3 5 5
17 Campaign 3 2 66
18 Campaign 3 100 7
19 Campaign 1 165 3
20 Campaign 1 321 13
21 Campaign 1 952 5
22 Campaign 1 13 3
23 Campaign 2 15 610
24 Campaign 2 0 15
25 Campaign 1 100 310
26 Campaign 2 165 0
27 Campaign 3 321 0
28 Campaign 3 952 1
29 Campaign 3 0 1
30 Campaign 3 5 0
I'd like to find out how many entries of 'Campaign 1' there were before the total of Time1 + Time2 was equal to or greater than a given number.
As an example, the result for Campaign 1 to reach 1400 should be 5.
Apologies if I haven't explained this clearly enough - the concept is still a little muddy at the moment.
Thanks
In SQL Server 2012, you can get the row using:
select t.*
from (select t.*, sum(time1 + time2) over (partition by name order by id) as cumsum
from table t
) t
where cumsum >= #VALUE and (cumsum - (time1 + time2)) < #VALUE;
You can get the count using:
select name, count(*)
from (select t.*, sum(time1 + time2) over (partition by name order by id) as cumsum
from table t
) t
where (cumsum - (time1 + time2)) < #VALUE
group by name;
If you are not using SQL Server 2012, you can do the cumulative sum with a correlated subquery:
select name, count(*)
from (select t.*,
(select sum(time1 + time2)
from table t2
where t2.name = t.name and
t2.id <= t.id
) as cumsum
from table t
) t
where (cumsum - (time1 + time2)) < #VALUE
group by name;
A recursive CTE computing a running total should work:
;WITH CTE AS
(
SELECT id,
name,
SUM([time]+[time 2])
OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS RunningTotal
FROM Table1
WHERE name = 'Campaign 1'
)
SELECT count(*)+1 AS [Count]
FROM CTE
WHERE RunningTotal < 1400
Note that I added 1 to the count as the query counts the number of rows needed to reach up to, but not including, 1400. Logic dictates that the next row will push the value above 1400.