SQL: Collapse sequential Data in to one row - sql

I am trying to Collapse sequential data in to one group. For Example :In below City1 data should show 2 rows.
Please help here.
CREATE TABLE #temp
(id INT NOT NULL IDENTITY(1, 1) ,
location1 VARCHAR(50)
)
INSERT INTO #temp VALUES ('City1')
INSERT INTO #temp VALUES ('City2')
INSERT INTO #temp VALUES ('City1')
INSERT INTO #temp VALUES ('City1')
INSERT INTO #temp VALUES ('City2')
INSERT INTO #temp VALUES ('City2')
SELECT * FROM #temp
Expected Output:
City1 1
city2 2
city1 3
city2 4

Please use like this. (Assuming you are using SQL 2012+)
solution 1
select location1, x1 from
(
select * , ROW_NUMBER() OVER (PARTITION BY x1 order by Id) rnk from
(
select * ,sum(p) over(order by id)+1 x1 from
(
select * , case when location1 = ISNULL(lag(location1) over (order by id),location1) then 0 else 1 end p
from temp2
)x
)k
)p where rnk = 1
OUTPUT
location1 x1
-------------------- -----------
City1 1
City2 2
City1 3
City2 4
(4 rows affected)

I think the most direct way to get what you want uses lag(). You seem to want where the changes occur:
select row_number() over (order by id) as new_id, location1
from (select t.*, lag(location1) over (order by id) as prev_location1
from #temp t
) t
where prev_location1 is null or prev_location1 <> location1;
Here is a rextester showing the solution.

Related

SQL - How to remove repeating values

My requirement is to remove the repeating values.
id name surname value
1 Vinduja Vijayan 5
3 Vinduja Vijayan 6
4 Vinduja Vijayan 7
Required output:
id name surname value
1 Vinduja Vijayan 5
3 NuLL Null 6
4 NULL NULL 7
This transformation should usually be applied in the application layer. It is possible to do in SQL, but not recommended, by using row_number() and case:
select id,
(case when row_number() over (partition by name, surname order by id) = 1
then name
end) as name,
(case when row_number() over (partition by name, surname order by id) = 1
then surname
end) as surname
from t
order by id;
Note that the final order by is very, very important. SQL result sets (like tables) are unordered by default. Without an explicit order by, the results could be in any order, and that would mess up your interpretation of the results.
DECLARE #table TABLE (
Id INT
,Name VARCHAR(20)
,Surname VARCHAR(20)
,value INT
);
INSERT into #table(ID,Name,Surname,value)
Select 1,'Vinduja','Vijayan',5
Union
Select 3,'Vinduja','Vijayan',6
Union
Select 4,'Vinduja','Vijayan',7
Select S.Id ,T.Name,T.Surname,S.value from (
Select * ,ROW_NUMBER() Over(Partition by name Order by name) [Row]
From #table)S
Left join #table T On T.Id =S.Id and S.[Row]=1
select
id,
case when rnk=1 then name end as name,
case when rnk=1 then surname end as surname ,
value
from
(
select
id,name,surname,value,
row_number()over(partition by name,surname order by id) as rnk
from table_name)repeatname
I'm not sure I understand your requirements. If you just want to display the data as described, then this won't work. But if you're trying to change the data in your table, this will do that.
DECLARE #Dupes TABLE
(
id INT
,name VARCHAR(30)
,surname VARCHAR(30)
,value INT
);
INSERT #Dupes
(
id
,name
,surname
,value
)
VALUES
(1, 'Vinduja', 'Vijayan', 5),
(3, 'Vinduja', 'Vijayan', 6),
(4, 'Vinduja', 'Vijayan', 7);
WITH cte AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY [name], surname ORDER BY id) AS RowNum
,id
,name
,surname
,value
FROM #Dupes
)
UPDATE cte
SET cte.name = NULL
,cte.surname = NULL
WHERE
cte.RowNum > 1;
SELECT *
FROM #Dupes;
--Results
+----+---------+---------+-------+
| id | name | surname | value |
+----+---------+---------+-------+
| 1 | Vinduja | Vijayan | 5 |
| 3 | NULL | NULL | 6 |
| 4 | NULL | NULL | 7 |
+----+---------+---------+-------+
And just for interest, using the LAG function. I assumed SQL Server.
select id,
iif(name = previous_name, null, name) name,
iif(surname = previous_surname, null, surname) surname
from (
select name, surname, id,
lag(name, 1, null) over (order by name, surname, id) previous_name,
lag(surname, 1, null) over (order by name, surname, id) previous_surname
from table_name ) a
order by a.name, a.surname, a.id

How to split an SQL Table into half and send the other half of the rows to new columns with SQL Query?

Country Percentage
India 12%
USA 20%
Australia 15%
Qatar 10%
Output :
Country1 Percentage1 Country2 Percentage2
India 12% Australia 15%
USA 20% Qatar 10%
For example there is a table Country which has percentages, I need to divide the table in Half and show the remaining half (i.e. the remaining rows) in the new columns. I've also provided the table structure in text.
First, this type of operation should be done at the application layer and not in the database. That said, it can be an interesting exercise to see how to do this in the database.
I would use conditional aggregation or pivot. Note that SQL tables are inherently unordered. Your base table has no apparent ordering, so the values could come out in any order.
select max(case when seqnum % 2 = 0 then country end) as country_1,
max(case when seqnum % 2 = 0 then percentage end) as percentage_1,
max(case when seqnum % 2 = 1 then country end) as country_2,
max(case when seqnum % 2 = 1 then percentage end) as percentage_2
from (select c.*,
(row_number() over (order by (select null)) - 1) as seqnum
from country c
) c
group by seqnum / 2;
Try this
declare #t table
(
Country VARCHAR(20),
percentage INT
)
declare #cnt int
INSERT INTO #T
VALUES('India',12),('USA',20),('Australia',15),('Quatar',12)
select #cnt = count(1)+1 from #t
;with cte
as
(
select
SeqNo = row_number() over(order by Country),
Country,
percentage
from #t
)
select
*
from cte c1
left join cte c2
on c1.seqno = (c2.SeqNo-#cnt/2)
and c2.SeqNo >= (#cnt/2)
where c1.SeqNo <= (#cnt/2)
My variant
SELECT 'A' Country,1 Percentage INTO #Country
UNION ALL SELECT 'B' Country,2 Percentage
UNION ALL SELECT 'C' Country,3 Percentage
UNION ALL SELECT 'D' Country,4 Percentage
UNION ALL SELECT 'E' Country,5 Percentage
;WITH numCTE AS(
SELECT
*,
ROW_NUMBER()OVER(ORDER BY Country) RowNum,
COUNT(*)OVER() CountOfCountry
FROM #Country
),
set1CTE AS(
SELECT Country,Percentage,ROW_NUMBER()OVER(ORDER BY Country) RowNum
FROM numCTE
WHERE RowNum<=CEILING(CountOfCountry/2.)
),
set2CTE AS(
SELECT Country,Percentage,ROW_NUMBER()OVER(ORDER BY Country) RowNum
FROM numCTE
WHERE RowNum>CEILING(CountOfCountry/2.)
)
SELECT
s1.Country,s1.Percentage,
s2.Country,s2.Percentage
FROM set1CTE s1
LEFT JOIN set2CTE s2 ON s1.RowNum=s2.RowNum
DROP TABLE #Country
I just wanted to try something. I have used the function OFFSET. It does the requirement i think for your sample data, but dont know if its bulletproof all the way:
SQL Code
declare #myt table (country nvarchar(50),percentage int)
insert into #myt
values
('India' ,12),
('USA' ,20),
('Australia' ,15),
('Qatar' ,10),
('Denmark',10)
DECLARE #TotalRows int
SET #TotalRows = (select CEILING(count(*) / 2.) from #myt);
WITH dataset1 AS (
SELECT *,ROW_NUMBER() over(order by country ) as rn from (
SELECT Country,percentage from #myt a
ORDER BY country OFFSET 0 rows FETCH FIRST #TotalRows ROWS ONLY
) z
)
,dataset2 AS (
SELECT *,ROW_NUMBER() over(order by country ) as rn from (
SELECT Country,percentage from #myt a
ORDER BY country OFFSET #TotalRows rows FETCH NEXT #TotalRows ROWS ONLY
) z
)
SELECT * FROM dataset1 a LEFT JOIN dataset2 b ON a.rn = b.rn
Result
Assuming you want descending alphabetic country names, but the left column is determined by where India is located in the result:
with CoutryCTE as (
select c.*
, row_number() over (order by country)-1 as rn
from country c
)
, Col as (
select rn % 2 as num from CoutryCTE
where Country = 'India'
)
select max(case when rn % 2 = Col.num then country end) as country_1
, max(case when rn % 2 = Col.num then percentage end) as percentage_1
, max(case when rn % 2 <> Col.num then country end) as country_2
, max(case when rn % 2 <> Col.num then percentage end) as percentage_2
from CoutryCTE
cross join Col
group by rn / 2
;
SQLFiddle Demo
| country_1 | percentage_1 | country_2 | percentage_2 |
|-----------|--------------|-----------|--------------|
| India | 12% | Australia | 15% |
| USA | 20% | Qatar | 10% |
nb: this is extremely similar to an earlier answer by Gordon Linoff

How to find max value from each group and display their information when using "group by"

For example, i create a table about people contribue to 2 campaigns
+-------------------------------------+
| ID Name Campaign Amount (USD) |
+-------------------------------------+
| 1 A 1 10 |
| 2 B 1 5 |
| 3 C 2 7 |
| 4 D 2 9 |
+-------------------------------------+
Task: For each campaign, find the person (Name, ID) who contribute the most to
Expected result is
+-----------------------------------------+
| Campaign Name ID |
+-----------------------------------------+
| 1 A 1 |
| 2 D 4 |
+-----------------------------------------+
I used "group by Campaign" but the result have 2 columns "Campagin" and "max value" when I need "Name" and "ID"
Thanks for your help.
Edited: I fix some values, really sorry
You can use analytic functions for this:
select name, id, amount
from (select t.*, max(amount) over (partition by campaign) as max_amount
from t
) t
where amount = max_amount;
You can also do it by giving a rank/row_number partiton by campaign and order by descending order of amount.
Query
;with cte as(
select [num] = dense_rank() over(
partition by [Campaign]
order by [Amount] desc
), *
from [your_table_name]
)
select [Campaign], [Name], [ID]
from cte
where [num] = 1;
Try the next query:-
SELECT Campaign , Name , ID
FROM (
SELECT Campaign , Name , ID , MAX (Amount)
FROM MyTable
GROUP BY Campaign , Name , ID
) temp;
Simply use Where Clause with the max of amount group by Campaign:-
As following generic code:-
select a, b , c
from tablename
where d in
(
select max(d)
from tablename
group by a
)
Demo:-
Create table #MyTable (ID int , Name char(1), Campaign int , Amount int)
go
insert into #MyTable values (1,'A',1,10)
insert into #MyTable values (2,'B',1,5)
insert into #MyTable values (3,'C',2,7)
insert into #MyTable values (4,'D',2,9)
go
select Campaign, Name , ID
from #MyTable
where Amount in
(
select max(Amount)
from #MyTable
group by Campaign
)
drop table #MyTable
Result:-
Please find the below code for the same
SELECT *
FROM #MyTable T
OUTER APPLY (
SELECT COUNT(1) record
FROM #MyTable T1
where t.Campaign = t1.Campaign
and t.amount < t1.amount
)E
where E.record = 0

The row must be shown same order in result set according to column value

I am using SQL Server 2008 R2.
I have the following data:
ID Value OrderNumber
1 A NULL
2 E 4
3 C NULL
4 B NULL
5 F 2
6 D NULL
i want to write a query that must fetch data ordering by OrderNumber column considering OrderNumber values. The query result must be below:
ID Value OrderNumber
1 A NULL
5 F 2 --indicates row must be second in result set.
3 C NULL
2 E 4 --indicates row must be fourth in result set.
4 B NULL
6 D NULL
Thanks for reading and your answers.
I've tried a number of different ways, but the only way I can find that produces the required results in a guaranteed way is:
declare #t table (ID int not null,Value char(1) not null,OrderNumber int null)
insert into #T(ID,Value,OrderNumber) values
(1,'A',NULL),
(2,'E',4),
(3,'C',NULL),
(4,'B',NULL),
(5,'F',2),
(6,'D',NULL)
;With Nbrs as (
select ROW_NUMBER() OVER (ORDER BY ID) as n from #t
), AvailableNbrs as (
select n,ROW_NUMBER() OVER (ORDER BY n) as rn from Nbrs where n not in (select OrderNumber from #t where OrderNumber is not null)
), RequiredOrders as (
select ID,ROW_NUMBER() OVER (ORDER BY ID) as rn from #t where OrderNumber is null
)
select
*,COALESCE(OrderNumber,an.n) as FinalOrder
from
#t t
left join
RequiredOrders ro
on
t.ID = ro.ID
left join
AvailableNbrs an
on
ro.rn = an.rn
order by COALESCE(OrderNumber,an.n)
Where we use a few CTEs to find OrderNumbers that aren't currently assigned, and to match those 1-1 with rows which have no OrderNumber.
Results:
|--------- #t --------------| |----- RequiredOrders ---------| |----- AvailableNbrs -------------------| |- COALESCE -------|
ID Value OrderNumber ID rn n rn FinalOrder
----------- ----- ----------- ----------- -------------------- -------------------- -------------------- --------------------
1 A NULL 1 1 1 1 1
5 F 2 NULL NULL NULL NULL 2
3 C NULL 3 2 3 2 3
2 E 4 NULL NULL NULL NULL 4
4 B NULL 4 3 5 3 5
6 D NULL 6 4 6 4 6
Found a much better solution than the accepted answer:
declare #t table(id int, value char, ordernumber int)
insert #t values(1,'A', null)
insert #t values(2,'E',4)
insert #t values(3,'C',NULL)
insert #t values(4,'B',NULL)
insert #t values(5,'F',2)
insert #t values(6,'D',NULL)
;with a as
(
select *, row_number() over (order by id)+.1 rn1 from #t
where ordernumber is null
union all
select *, ordernumber - rank() over (order by ordernumber)+1 rn1 from #t
where ordernumber is not null
)
select * from a order by rn1, ordernumber
Try this:
SELECT *
FROM tableName
Order BY CASE WHEN OrderNumber IS NULL THEN ID ELSE OrderNumber END

INSERT INTO statement that copies rows and auto-increments non-identity key ID column

Given a table that has three columns
ID (Primary Key, not-autoincrementing)
GroupID
SomeValue
I am trying to write a single SQL INSERT INTO statement that will make a copy of every row that has one GroupID into a new GroupID.
Example beginning table:
ID | GroupID | SomeValue
------------------------
1 | 1 | a
2 | 1 | b
Goal after I run a simple INSERT INTO statement:
ID | GroupID | SomeValue
------------------------
1 | 1 | a
2 | 1 | b
3 | 2 | a
4 | 2 | b
I thought I could do something like:
INSERT INTO MyTable
( [ID]
,[GroupID]
,[SomeValue]
)
(
SELECT (SELECT MAX(ID) + 1 FROM MyTable)
,#NewGroupID
,[SomeValue]
FROM MyTable
WHERE ID = #OriginalGroupID
)
This causes a PrimaryKey violation since it will end up reusing the same Max(ID)+1 value multiple times as it seems.
Is my only recourse to a bunch of INSERT statements in a T-SQL WHILE statement that has an incrementing Counter value?
I also don't have the option of turning the ID into an auto-incrementing Identity column since that would breaking code I don't have source for.
Instead of + 1, add the row number. I also fixed the error in your WHERE clause (should be GroupID =, not ID =):
INSERT INTO MyTable
( [ID]
,[GroupID]
,[SomeValue]
)
(
SELECT
(SELECT MAX(ID) FROM MyTable) + ROW_NUMBER() OVER (ORDER BY GroupId),
#NewGroupID,
[SomeValue]
FROM MyTable
WHERE GroupID = #OriginalGroupID
)
WITH q AS
(
SELECT *,
(
SELECT MAX(id)
FROM mytable
) + ROW_NUMBER() OVER () AS nid
FROM mytable
WHERE groupID = 1
)
INSERT
INTO mytable (id, groupid, somevalue)
SELECT nid, 2, somevalue
FROM q
Use this:
INSERT INTO MyTable
( [ID]
,[GroupID]
,[SomeValue]
)
SELECT ROW_NUMBER() OVER() + X.MaxID
,#NewGroupID
,[SomeValue]
FROM MyTable
CROSS JOIN (SELECT MAX(ID) AS MaxID FROM MyTable) X
WHERE GroupID = #OriginalGroupID