Split Columns into two equal number of Rows - sql

I have the table structure below,
I need to merge the CouponNumber to two equal as CouponNumber1 and CouponNumber2 as shown in the figure
SELECT Name, MobileNumber, CouponNumber, IsDispatched, Status
FROM CouponInvoicePrescription
This is my query.

Try this:
WITH
input(ord,name,mobno,couponno,isdispatched,status) AS (
SELECT 0,'amar',8888888888,'CPever901',FALSE,1
UNION ALL SELECT 1,'amar',8888888888,'CP00005' ,FALSE,1
UNION ALL SELECT 2,'pt3' ,7777777777,'cp9090' ,FALSE,1
UNION ALL SELECT 3,'pt3' ,7777777777,'ev2' ,FALSE,1
UNION ALL SELECT 4,'pt3' ,7777777777,'cp9909' ,FALSE,1
UNION ALL SELECT 5,'pt3' ,7777777777,'cp10' ,FALSE,1
)
SELECT
name
, MAX(CASE ord % 2 WHEN 1 THEN couponno END) AS couponno1
, MAX(CASE ord % 2 WHEN 0 THEN couponno END) AS couponno2
, isdispatched
, status
FROM input
GROUP BY
ord / 2
, name
, isdispatched
, status
ORDER BY 1
-- out name | couponno1 | couponno2 | isdispatched | status
-- out ------+-----------+-----------+--------------+--------
-- out amar | CP00005 | CPever901 | f | 1
-- out pt3 | cp10 | cp9909 | f | 1
-- out pt3 | ev2 | cp9090 | f | 1

Try this:
SELECT * FROM
(
SELECT
sub.rn,
sub.Name,
sub.MobileNumber,
sub.CouponNumber as CouponNumber1,
LEAD(sub.CouponNumber,1) OVER (PARTITION BY sub.MobileNumber ORDER BY sub.rn) as CouponNumber2,
sub.IsDispatched,
sub.Status
FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION by MobileNumber ORDER BY Name) as rn,
*
FROM
input
) sub
)
WHERE rn % 2 <> 0

Related

How put grouping variable to columns in SQL/

I have following dataset
and want to get this
How can I do it?
Using SQL Server, you can use a PIVOT, such as :
SELECT Time, [a],[b],[c]
FROM
(
SELECT time, [group],value
FROM dataset) d
PIVOT
(
SUM(value)
FOR [group] IN ([a],[b],[c])
) AS pvt
You can try it on the following fiddle.
Changed the column names to not conflict with reserved words. You would have to put them into single quotes otherwise.
WITH
-- the input
indata(grp,tm,val) AS (
SELECT 'a',1,44
UNION ALL SELECT 'a',2,22
UNION ALL SELECT 'a',3, 1
UNION ALL SELECT 'b',1, 1
UNION ALL SELECT 'b',2, 5
UNION ALL SELECT 'b',3, 6
UNION ALL SELECT 'c',1, 7
UNION ALL SELECT 'c',2, 8
UNION ALL SELECT 'c',3, 9
)
SELECT tm
, SUM(CASE grp WHEN 'a' THEN val END) AS a
, SUM(CASE grp WHEN 'b' THEN val END) AS b
, SUM(CASE grp WHEN 'c' THEN val END) AS c
FROM indata
GROUP BY tm
;
tm | a | b | c
----+----+---+---
1 | 44 | 1 | 7
2 | 22 | 5 | 8
3 | 1 | 6 | 9
select * from
(
select
time,[group],value
from yourTable
group by time,[group],value
)
as table
pivot
(
sum([value])
for [group] in ([a],[b],[c])
) as p
order by time
This is the result
for Vertica,
SELECT time
, SUM(value) FILTER (WHERE group = a) a
, SUM(value) FILTER (WHERE group = b) b
, SUM(value) FILTER (WHERE group = c) c
FROM yourTable
GROUP BY time

Ranking counts using an SQL query

I am using DB Browser for SQLite. I have a table in the following format:
+-----------+-------------------------------------+
| search_id | search_town |
+-----------+-------------------------------------+
| 1 | town1,town3 |
| 2 | town2,town4,town5 |
| 3 | town3,town5 |
| 4 | town2,town5 |
| 5 | town2,town3,town4 |
+-----------+-------------------------------------+
I would like to do a COUNT on the number of times town1 through town5 has appeared under search_town, and then rank in descending order the towns based on their respective counts. So far I have the following query:
SELECT SUM(CASE WHEN search_location LIKE '%town01%' THEN 1 ELSE 0 END) AS town01,
SUM(CASE WHEN search_location LIKE '%town02%' THEN 1 ELSE 0 END) AS town02,
SUM(CASE WHEN search_location LIKE '%town03%' THEN 1 ELSE 0 END) AS town03,
SUM(CASE WHEN search_location LIKE '%town04%' THEN 1 ELSE 0 END) AS town04,
SUM(CASE WHEN search_location LIKE '%town05%' THEN 1 ELSE 0 END) AS town05
FROM searches
...but am unable to do an ORDER BY as the towns and their counts are output as columns instead of rows in this format
+-------+-------+-------+-------+-------+
| town1 | town2 | town3 | town4 | town5 |
+-------+-------+-------+-------+-------+
| 12 | 31 | 12 | 24 | 12 |
+-------+-------+-------+-------+-------+
Is there another approach to this? Appreciate any comments.
You are turning your output in a single row using CASE WHEN, to convert it into multiple rows, you can try like following.
;WITH cte
AS (SELECT *
FROM (VALUES ('Town1'),
('Town2'),
('Town3'),
('Town4'),
('Town5')) T(town))
SELECT Count(*) [Count],
C.town
FROM [TABLE_NAME] T
INNER JOIN cte C
ON T.search_location LIKE '%' + C.town + '%'
GROUP BY C.town
ORDER BY Count(*) DESC
Online DEMO
Another approach can be using UNION ALL.
SELECT *
FROM (SELECT Count(*) s,
'Town1' AS Col
FROM tablename
WHERE search_location LIKE '%town1%'
UNION ALL
SELECT Count(*) s,
'Town2' AS Col
FROM tablename
WHERE search_location LIKE '%town2%'
UNION ALL
SELECT Count(*) s,
'Town3' AS Col
FROM tablename
WHERE search_location LIKE '%town3%'
UNION ALL
SELECT Count(*) s,
'Town4' AS Col
FROM tablename
WHERE search_location LIKE '%town4%'
UNION ALL
SELECT Count(*) s,
'Town5' AS Col
FROM tablename
WHERE search_location LIKE '%town5%') t
ORDER BY s DESC
You can use a recursive common-table expression (CTE) to turn the comma-separated list into a set of rows. When the table is normalized, you can group by town and sort by descending count:
WITH rec(town, remain)
AS (
SELECT SUBSTR(search_town, 0, INSTR(search_town, ',')) -- Before ,
, SUBSTR(search_town, INSTR(search_town, ',')+1) || ',' -- After ,
FROM t1
UNION ALL
SELECT SUBSTR(remain, 0, INSTR(remain, ',')) -- Before ,
, SUBSTR(remain, INSTR(remain, ',')+1) -- After ,
FROM rec
WHERE LENGTH(remain) > 0
)
SELECT town
, COUNT(*)
FROM rec
GROUP BY
town
ORDER BY
COUNT(*) DESC
Idea from this blog post. Working example at sqliteonline.

How to apply pivot to result of query

There is my current query:
SELECT Name, Code, Today
, Account || Currency as Accounts
FROM (
SELECT
b.description AS Name
, b.contragentidentifycode AS Code
, c.systemday AS Today
, b.accountno AS Account
, b.currencysname AS Currency
FROM vAACCOUNT b, currentdaysetting c
WHERE b.contragentid = 412
AND b.accountno LIKE '26%'
)
it gives me such result:
Name | Code | Today | Accounts
---------------------------------------
name1 | code1 | 07.09.2016 | acc1+curr1
name1 | code1 | 07.09.2016 | acc2+curr1
name1 | code1 | 07.09.2016 | acc1+curr2
name1 | code1 | 07.09.2016 | acc2+curr2
name1 | code1 | 07.09.2016 | acc1+curr3
name1 | code1 | 07.09.2016 | acc2+curr3
name1 | code1 | 07.09.2016 | acc1+curr4
name1 | code1 | 07.09.2016 | acc2+curr4
I need convert this view to:
Name | Code | Today | someName1 | someName2 | someName3 | someName4 | someName5 | someName6 | someName7 | someName8
-------------------------------------------------------------------------------------------------------------------------------------------
name1 | code1 | 07.09.2016 | acc1+curr1 | acc2+curr1 | acc1+curr2 | acc2+curr2 | acc1+curr3 | acc2+curr3 | acc1+curr4 | acc2+curr4
I guess that most probably for this I have to use the keyword "Pivot". But all my attempts to do so - have failed. I can not to project what I see in the examples, to my table. Please help.
For number of columns I can add such "id" column:
SELECT id, Name, Code, Today
, Account || Currency as Accounts
FROM (
SELECT
row_number() over (ORDER BY b.id) AS id
, b.description AS Name
...
In my scenario:
numbers of accounts may be different;
name, code and data - one per query;
combination of accaunt+currency are unique;
result should be in one line;
total number of lines in result of query, cannot be more then 10 (in my example 8)
Per my comment above, I don't think PIVOT works for you. The answer from #RoundFour works, but requires that you know, and code for, all possible values for Account || Currency. This suggests there will never be new values for these items - I find that unlikely.
The following will allow you to switch the shape of your data. It makes no assumptions about the values in your data, but it does assume a limit on the number of possible combinations - I have coded for eight.
WITH account_data (name,code,today,account)
AS
(
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc1+curr1' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc2+curr1' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc1+curr2' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc2+curr2' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc1+curr3' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc2+curr3' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc1+curr4' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc2+curr4' FROM dual UNION ALL
SELECT 'name2','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc1+curr1' FROM dual UNION ALL
SELECT 'name2','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc2+curr1' FROM dual UNION ALL
SELECT 'name2','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc1+curr2' FROM dual UNION ALL
SELECT 'name3','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc2+curr2' FROM dual
)
SELECT
name
,code
,today
,MAX(account1)
,MAX(account2)
,MAX(account3)
,MAX(account4)
,MAX(account5)
,MAX(account6)
,MAX(account7)
,MAX(account8)
FROM
(SELECT
name
,code
,today
,CASE
WHEN rn = 1 THEN account
END account1
,CASE
WHEN rn = 2 THEN account
END account2
,CASE
WHEN rn = 3 THEN account
END account3
,CASE
WHEN rn = 4 THEN account
END account4
,CASE
WHEN rn = 5 THEN account
END account5
,CASE
WHEN rn = 6 THEN account
END account6
,CASE
WHEN rn = 7 THEN account
END account7
,CASE
WHEN rn = 8 THEN account
END account8
FROM
(SELECT
name
,code
,today
,account
,ROW_NUMBER() OVER (PARTITION BY name ORDER BY account) rn
FROM
account_data
)
)
GROUP BY
name
,code
,today
;
UPDATE >>>>>>>>>
The WITH... clause above is just because I don't have your tables and data in my system. I've rewritten my answer using your query as a guide - please note I have not been able to test this ...
SELECT
name
,code
,today
,MAX(account1)
,MAX(account2)
,MAX(account3)
,MAX(account4)
,MAX(account5)
,MAX(account6)
,MAX(account7)
,MAX(account8)
FROM
(SELECT
name
,code
,today
,CASE
WHEN rn = 1 THEN account
END account1
,CASE
WHEN rn = 2 THEN account
END account2
,CASE
WHEN rn = 3 THEN account
END account3
,CASE
WHEN rn = 4 THEN account
END account4
,CASE
WHEN rn = 5 THEN account
END account5
,CASE
WHEN rn = 6 THEN account
END account6
,CASE
WHEN rn = 7 THEN account
END account7
,CASE
WHEN rn = 8 THEN account
END account8
FROM
(SELECT
b.description AS Name
,b.contragentidentifycode AS Code
,c.systemday AS Today
,b.accountno AS Account
,b.currencysname AS Currency
,b.accountno || b.currencysname AS Accounts
,ROW_NUMBER() OVER (PARTITION BY b.description ORDER BY b.accountno) rn
FROM vAACCOUNT b, currentdaysetting c
WHERE b.contragentid = 412
AND b.accountno LIKE '26%'
)
)
GROUP BY
name
,code
,today
;
If you know all the account+currency combinations you can use this pivot (I only implemented 3 of them here):
select *
from (
<your-query> )
pivot (
min(accounts) as accounts FOR (accounts) in ('acc1+curr1' as a, 'acc2+curr1' as b, 'acc1+curr2' c)
);
There is my pivot solution:
SELECT *
FROM (
SELECT id, Name, Code, Today, Account || Currency as Accounts
FROM (
SELECT
row_number() over (ORDER BY b.id) AS id
, b.description AS Name
, b.contragentidentifycode AS Code
, c.systemday AS Today
, b.accountno AS Account
, b.currencysname AS Currency
FROM vAACCOUNT b, currentdaysetting c
WHERE b.contragentid = 412
AND b.accountno LIKE '26%'
)
)
pivot (
MIN(Accounts)
FOR ID IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
) pvt

T-sql rank for max and min value

I need help with a t-sql query.
I have a table with this structure:
id | OverallRank | FirstRank | SecondRank | Nrank..
1 | 10 | 20 | 30 | 5
2 | 15 | 24 | 12 | 80
3 | 10 | 40 | 37 | 12
I need a query that produces this kind of result:
When id: 1
id | OverallRank | BestRankLabel | BestRankValue | WorstRankLabel | WorkRankValue
1 | 10 | SecondRank | 30 | Nrank | 5
When id: 2
id | OverallRank | BestRankLabel | BestRankValue | WorstRankLabel | WorkRankValue
1 | 15 | FirstRank | 24 | SecondRank | 12
How can I do it?
Thanks in advance
with cte(id, RankValue,RankName) as (
SELECT id, RankValue,RankName
FROM
(SELECT id, OverallRank, FirstRank, SecondRank, Nrank
FROM ##input) p
UNPIVOT
(RankValue FOR RankName IN
(OverallRank, FirstRank, SecondRank, Nrank)
)AS unpvt)
select t1.id, max(case when RankName = 'OverallRank' then RankValue else null end) as OverallRank,
max(case when t1.RankValue = t2.MaxRankValue then RankName else null end) as BestRankName,
MAX(t2.MaxRankValue) as BestRankValue,
max(case when t1.RankValue = t3.MinRankValue then RankName else null end) as WorstRankName,
MAX(t3.MinRankValue) as WorstRankValue
from cte as t1
left join (select id, MAX(RankValue) as MaxRankValue from cte group by id) as t2 on t1.id = t2.id
left join (select id, min(RankValue) as MinRankValue from cte group by id) as t3 on t1.id = t3.id
group by t1.id
Working good with your test data. You should only edit RankName IN (OverallRank, FirstRank, SecondRank, Nrank) by adding right columns' names.
CASE
WHEN OverallRank > FirstRank and OverallRank > FirstSecondRand and OverallRank > nRank THEN 'OverallRank'
WHEN FirstRank > OverallRank ... THEN 'FirstRank'
END
This kind of query is why you should normalise your data.
declare #id int, #numranks int
select #id = 1, #numranks = 3 -- number of Rank columns
;with cte as
(
select *
from
(
select *,
ROW_NUMBER() over (partition by id order by rank desc) rn
from
(
select * from YourBadlyDesignedTable
unpivot (Rank for RankNo in (FirstRank, SecondRank, ThirdRank))u -- etc
) v2
) v1
where id=#id and rn in (1, #numranks)
)
select
tMin.id,
tMin.OverallRank,
tMin.RankNo as BestRankLabel,
tMin.Rank as BestRankValue,
tMax.RankNo as WorstRankLabel,
tMax.Rank as WorstRankValue
from (select * from cte where rn=1) tMin
inner join (select * from cte where rn>1) tMax
on tMin.id = tmax.id
You can take out the id = #id if you want all rows.

Select and merge rows in a table in SQL Stored procedure

Have a temp table with schema: ID | SeqNo | Name
ID - Not unique
SeqNo - Int (can be 1,2 or 3). Sort of ID+SeqNo as Primary key
Name - Any text
And sample data in the table like this
1 | 1 | RecordA
2 | 1 | RecordB
3 | 1 | RecordC
1 | 2 | RecordD
4 | 1 | RecordE
5 | 1 | RecordF
3 | 1 | RecordG
Need to select from this table and output like
1 | RecordA/RecordD
2 | RecordB
3 | RecordC/RecordG
4 | RecordE
5 | RecordF
Need to do this without cursor.
If SeqNo is limited to 1,2,3:
select id, a.name + coalesce('/'+b.name, '') + coalesce('/'+c.name, '')
from myTable a
left outer join myTable b on a.id=b.id and b.seqno = 2
left outer join myTable c on a.id=c.id and c.seqno = 3
where a.seqno = 1;
If SeqNo is open ended you can deploy a recursive cte:
;with anchor as (
select id, name, seqno
from myTable
where seqno=1)
, recursive as (
select id, name, seqno
from anchor
union all
select t.id, r.name + '/' + t.name, t.seqno
from myTable t
join recursive r on t.id = r.id and r.seqno+1 = t.seqno)
select id, name from recursive;
If you know SeqNo will never be more than 3:
select Id, Names = stuff(
max(case when SeqNo = 1 then '/'+Name else '' end)
+ max(case when SeqNo = 2 then '/'+Name else '' end)
+ max(case when SeqNo = 3 then '/'+Name else '' end)
, 1, 1, '')
from table1
group by Id
Otherwise, something like this is the generic solution to an arbitrary number of items:
select Id, Names = stuff((
select '/'+Name from table1 b
where a.Id = b.Id order by SeqNo
for xml path (''))
, 1, 1, '')
from table1 a
group by Id
Or write a CLR UDA.
Edit: had the wrong alias on the correlated table!
Edit2: another version, based on Remus's recursion example. I couldn't think of any way to select only the last recursion per Id, without aggregation or sorting. Anybody know?
;with
myTable as (
select * from (
values
(1, 1, 'RecordA')
, (2, 1, 'RecordB')
, (3, 1, 'RecordC')
, (1, 2, 'RecordD')
, (4, 1, 'RecordE')
, (5, 1, 'RecordF')
, (3, 2, 'RecordG')
) a (Id, SeqNo, Name)
)
, anchor as (
select id, name = convert(varchar(max),name), seqno
from myTable where seqno=1
)
, recursive as (
select id, name, seqno
from anchor
union all
select t.id, r.name + '/' + t.name, t.seqno
from myTable t
join recursive r on t.id = r.id and r.seqno+1 = t.seqno
)
select id, name = max(name)
from recursive
group by id;
---- without aggregation, we get 7 rows:
--select id, name
--from recursive;
The solution is good. I have a similar issue, but here I am using 2 different tables. ex:
table1
1 | 1 |
2 | 3 |
3 | 1 |
4 | 2 |
5 | 1 |
1 | 2 |
1 | 3 |
4 | 1 |
5 | 2 |
2 | 2 |
4 | 3 |
table2
1 | RecordA
2 | RecordB
3 | RecordC
I want to get the data from two tables and display in the below format.
1 | RecordA,RecordB,RecordC|
2 | RecordB,RecordC|
3 | RecordA |
4 | RecordA,RecordB,RecordC|
5 | RecordA,RecordB |