Two rows in one in SQL server - sql

The answer is too close, Thanks
But
The problem is that if too many records to be entered
| id | name | age | Tel
------------------------------------------
1 | 1 | Frank | 40 | null
2 | 1 | null | 50 | 7834xx
3 | 1 | Alex | null | null
4 | 1 | null | 20 | null
5 | 2 | James | null | 4121xx
Query return the Maximum value
Like:
| id | name | age | Tel
------------------------------------------
1 | 1 | Frank | 50 | 7834xx
i need Select Query like this:
| id | name | age | Tel
------------------------------------------
1 | 1 | Alex | 20 | 7834xx
what do I do? Plz?

Here's a roundabout way to combine the last non-empty value of 3 columns:
-- Using a table variable for test data
declare #Test table (tableId int identity(1,1), id int, name varchar(100), age int, tel varchar(30));
insert into #Test (id, name, age, tel) values
(1,'Frank',40,null),
(1,null,50,'7834xx'),
(1,'Alex',null,null),
(1,null,20,null),
(2,'James',null,'4121xx');
select n.id, n.name, a.age, t.tel
from (
select top(1) with ties id, name
from #Test
where name is not null
order by row_number() over (partition by id order by tableId desc)
) n
inner join (
select top(1) with ties id, age
from #Test
where age is not null
order by row_number() over (partition by id order by tableId desc)
) a on (n.id = a.id)
inner join (
select top(1) with ties id, tel
from #Test
where tel is not null
order by row_number() over (partition by id order by tableId desc)
) t on (n.id = t.id);
or by re-using a CTE
;with CTE AS (
select * ,
row_number() over (partition by id, iif(name is not null,1,0) order by tableId desc) as rn_name,
row_number() over (partition by id, iif(age is not null,1,0) order by tableId desc) as rn_age,
row_number() over (partition by id, iif(tel is not null,1,0) order by tableId desc) as rn_tel
from #Test
)
select n.id, n.name, a.age, t.tel
from CTE n
join CTE a on (a.id = n.id and a.age is not null and a.rn_age = 1)
join CTE t on (t.id = n.id and t.tel is not null and t.rn_tel = 1)
where (n.name is not null and n.rn_name = 1);
Result :
╔════╦══════╦═════╦════════╗
║ id ║ name ║ age ║ tel ║
╠════╬══════╬═════╬════════╣
║ 1 ║ Alex ║ 20 ║ 7834xx ║
╚════╩══════╩═════╩════════╝
After looking at this answer more than a year later.
You could also use the window function first_value for this.
Without using any join.
select Id, name, age, tel
from
(
select Id
, row_number() over (partition by id order by tableId desc) as rn
, first_value(name) over (partition by id order by iif(name is null,1,0), tableId desc) as name
, first_value(age) over (partition by id order by iif(age is null,1,0), tableId desc) as age
, first_value(tel) over (partition by id order by iif(tel is null,1,0), tableId desc) as tel
from #Test
) q
where rn = 1
and name is not null and age is not null and tel is not null;

One simple way is to get max as below:
Select Id, Max(name) as [Name], Max(age) as Age, Max(Tel) as Tel
from yourtable
Group by Id

thanks for answering and helping.
i find this query:
SELECT TOP 10
(SELECT top(1) name FROM test1 where id=1 and name is not null order by autoID desc) as name
,(SELECT top(1) age FROM test1 where id=1 and age is not null order by autoID desc) as Age
,(SELECT top(1) Tel FROM test1 where id=1 and Tel is not null order by autoID desc) as Telephon
FROM [dbo].[test1]
group by id
its worked !!!
but i thing there are another Easy Way , maybe like this:
Select Id, NotNull(name) as [Name], NotNull(age) as Age, NotNull(Tel) as Tel
from yourtable Group by Id
???

Related

Get top 5 records for each group and Concate them in a Row per group

I have a table Contacts that basically looks like following:
Id | Name | ContactId | Contact | Amount
---------------------------------------------
1 | A | 1 | 12323432 | 555
---------------------------------------------
1 | A | 2 | 23432434 | 349
---------------------------------------------
2 | B | 3 | 98867665 | 297
--------------------------------------------
2 | B | 4 | 88867662 | 142
--------------------------------------------
2 | B | 5 | null | 698
--------------------------------------------
Here, ContactId is unique throughout the table. Contact can be NULL & I would like to exclude those.
Now, I want to select top 5 contacts for each Id based on their Amount. I am accomplished that by following query:
WITH cte AS (
SELECT id, Contact, amount, ROW_NUMBER()
over (
PARTITION BY id
order by amount desc
) AS RowNo
FROM contacts
where contact is not null
)
select *from cte where RowNo <= 5
It's working fine upto this point. Now I want to concate these (<=5) record for each group & show them in a single row by concatenating them.
Expected Result :
Id | Name | Contact
-------------------------------
1 | A | 12323432;23432434
-------------------------------
2 | B | 98867665;88867662
I am using following query to achieve this but it still gives all records in separate rows and also including Null values too:
WITH cte AS (
SELECT id, Contact, amount,contactid, ROW_NUMBER()
over (
PARTITION BY id
order by amount desc
) AS RowNo
FROM contacts
where contact is not null
)
select *from id, name,
STUFF ((
SELECT distinct '; ' + isnull(contact,'') FROM cte
WHERE co.id= cte.id and co.contactid= cte.contactid
and RowNo <= 5
FOR XML PATH('')),1, 1, '')as contact
from contacts co inner join cte where cte.id = co.id and co.contactid= cte.contactid
Above query still gives me all top 5 contacts in diff rows & including null too.
Is it a good idea to use CTE and STUFF togather? Please suggest if there is any better approach than this.
I got the problem with my final query:
I don't need original Contact table in my final Select, since I already have everything I needed in CTE. Also, Inside STUFF(), I'm using contactid to join which is what actually I'm trying to concat here. Since I'm using that condition for join, I am getting records in diff rows. I've removed these 2 condition and it worked.
WITH cte AS (
SELECT id, Contact, amount,contactid, ROW_NUMBER()
over (
PARTITION BY id
order by amount desc
) AS RowNo
FROM contacts
where contact is not null
)
select *from id, name,
STUFF ((
SELECT distinct '; ' + isnull(contact,'') FROM cte
WHERE co.id= cte.id
and RowNo <= 5
FOR XML PATH('')),1, 1, '')as contact
from cte where rowno <= 5
You can use conditional aggregation:
id, name, contact,
select id, name,
concat(max(case when seqnum = 1 then contact + ';' end),
max(case when seqnum = 2 then contact + ';' end),
max(case when seqnum = 3 then contact + ';' end),
max(case when seqnum = 4 then contact + ';' end),
max(case when seqnum = 5 then contact + ';' end)
) as contacts
from (select c.*
row_number() over (partition by id order by amount desc) as seqnum
from contacts c
where contact is not null
) c
group by id, name;
If you are running SQL Server 2017 or higher, you can use string_agg(): as most other aggregate functions, it ignores null values by design.
select id, name, string_agg(contact, ',') within group (order by rn) all_contacts
from (
select id, name, contact
row_number() over (partition by id order by amount desc) as rn
from contacts
where contact is not null
) t
where rn <= 5
group by id, name
Note that you don't strictly need a CTE here; you can return the columns you need from the subquery, and use them directly in the outer query.
In earlier versions, one approach using stuff() and for xml path is:
with cte as (
select id, name, contact,
row_number() over (partition by id order by amount desc) as rn
from contacts
where contact is not null
)
select id, name,
stuff(
(
select ', ' + c1.concat
from cte c1
where c1.id = c.id and c1.rn <= 5
order by c1.rn
for xml path (''), type
).value('.', 'varchar(max)'), 1, 2, ''
) all_contacts
from cte
group by id, name
I agree with #GMB. STRING_AGG() is what you need ...
WITH
contacts(Id,nm,ContactId,Contact,Amount) AS (
SELECT 1,'A',1,12323432,555
UNION ALL SELECT 1,'A',2,23432434,349
UNION ALL SELECT 2,'B',3,98867665,297
UNION ALL SELECT 2,'B',4,88867662,142
UNION ALL SELECT 2,'B',5,NULL ,698
)
,
with_filter_val AS (
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY id ORDER BY amount DESC) AS rn
FROM contacts
)
SELECT
id
, nm
, STRING_AGG(CAST(contact AS CHAR(8)),',') AS contact_list
FROM with_filter_val
WHERE rn <=5
GROUP BY
id
, nm
-- out id | nm | contact_list
-- out ----+----+-------------------
-- out 1 | A | 12323432,23432434
-- out 2 | B | 98867665,88867662

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

Procedure to copy data from a table to another table in SQL Server

I have a table A, with 4 columns:
first_name, invoice, value, date.
And a table B (first_name, max_invoice_name, max_invoice_value, last_date)
I want to create a procedure in order to move data from A, to B, but:
first_name should be one time in B,
max_invoice_name is the name of the max invoice value
max_invoice_value is the max value
last_date is the latest date from invoices from the same first_name.
For example:
TABLE A:
Smith | Invoice1 | 100 | 23.06.2016
John | Invoice13 | 23 | 18.07.2016
Smith | Invoice3 | 200 | 01.01.2015
Table B should be:
Smith |Invoice3 | 200 | 23.06.2016
John |Invoice13| 23 | 18.07.2016
Something like this should work:
select *, (select max(date) from #Table1 T1 where T1.first_name = X.first_name)
from (
select
*,
row_number() over (partition by first_name order by invoice_Value desc) as RN
from
#Table1
) X
where RN = 1
Row number takes care of selecting the row with biggest value, and the max get's the date. You'll need to list the columns in correct place instead of *
You will need to create 2 scalar functions getMaxNameForMaxValue AND getLastDateByFirstName to get the values you want.
INSERT INTO TableB (first_name, max_invoice_name, max_invoice_value, last_date) (SELECT DISTINCT first_name, getMaxNameForMaxValue(MAX(max_value)) AS 'max_invoice_name', MAX(max_invoice_value) AS 'max_invoice_value', getLastDateByFirstName(first_name) AS 'lastDate' FROM Table A)
You can use something like this:
--INSERT INTO TableB
SELECT first_name,
invoice_name,
invoice_value,
last_date
FROM (
SELECT a.first_name,
a.invoice_name,
a.invoice_value,
COALESCE(p.last_date,a.last_date) as last_date,
ROW_NUMBER() OVER (PARTITION BY a.first_name ORDER BY a.last_date) as rn
FROM TableA a
OUTER APPLY (SELECT TOP 1 * FROM TableA WHERE first_name = a.first_name and last_date > a.last_date) as p
) as res
WHERE rn = 1
As output:
first_name invoice_name invoice_value last_date
John Invoice13 23 2016-07-18
Smith Invoice3 200 2016-06-23
Try this
Insert into TableB(first_name, max_invoice_name, max_invoice_value, last_date)
select t1.first_name,t1.invoice,t1,value,t2.date from TableA as t1 inner join
(
select first_name, max(replace(invoice,'invoice','')) as invoice, max(date) as date
from TableA group by first_name
) as t2 on t1.first_name=t2.first_name and t1.invoice=t2.invoice

Get latest rows by date from aggregate

Hey i'm kinda stuck with this query. Using SQL-server
i have in the table, UNIQUE(date, medId, userId)
I have this table
date | medId | userId | Quantity
2016-06-10 | 2 | 1 | 28
2016-06-07 | 1 | 1 | 19
2016-06-06 | 1 | 1 | 10
i want to get the row with the max date, per group of medId,userId, in this case
i would get
2016-06-10 | 2 | 1 | 28
2016-06-07 | 1 | 1 | 19
thanks in advance!
i've tried this
SELECT
a.userMedStockDate,
a.userMedStockMedId,
a.userMedStockUserId,
a.userMedStockQuantity
FROM (SELECT
MAX(userMedStockDate) AS userMedStockDate,
userMedStockQuantity,
userMedStockUserId,
userMedStockMedId,
ROW_NUMBER() OVER (partition by userMedStockMedId,userMedStockUserId
ORDER BY MAX(userMedStockDate) desc) AS rnk
FROM UserMedStock
GROUP BY
userMedStockUserId,
userMedStockQuantity,
userMedStockMedId) a
WHERE a.rnk = 1
[SOLVED]
this should work
select * from
(
select
[date] , medId, userId ,Quantity
,row_number() over (partition by medId, userId order by [date] desc) as rowid
from yourtable
) as x
where rowid = 1
Could also try this:
select y.* from
table1 y inner join
(
SELECT [Date] = MAX([Date]), medId, userId
FROM table1
GROUP BY medId, userId
) x on y.[Date] = x.[Date] and y.medId = x.medId and y.userId = x.userId
i changed the fields to my actual table but here
SELECT
a.userMedStockDate, a.userMedStockMedId, a.userMedStockUserId, a.userMedStockQuantity
FROM(
SELECT
MAX(userMedStockDate) AS userMedStockDate,
userMedStockQuantity,
userMedStockUserId,
userMedStockMedId,
ROW_NUMBER()OVER(partition by userMedStockMedId, userMedStockUserId ORDER BY MAX(userMedStockDate) desc) AS rnk
FROM UserMedStock
GROUP BY userMedStockUserId, userMedStockQuantity, userMedStockMedId
) a
WHERE a.rnk = 1

Oracle SQL: Transform rows to multiple columns

I'm using Oracle 11G and need a way to turn rows into new groups of columns in a select statement. We're transitioning to a 1:3 relationship for some of our data and need a way to get it into a view. Can you help us transform data that looks like this:
+---------+------------+
| User_Id | Station_Id |
+---------+------------+
| 1 | 203 |
| 1 | 204 |
| 2 | 203 |
| 3 | 487 |
| 3 | 3787 |
| 3 | 738 |
+---------+------------+
into this:
+---------+-------------+-------------+---------------+
| User_Id | Station_One | Station_Two | Station_Three |
+---------+-------------+-------------+---------------+
| 1 | 203 | 204 | Null |
| 2 | 203 | Null | Null |
| 3 | 487 | 3787 | 738 |
+---------+-------------+-------------+---------------+
Let me know what ever other specifics you would like and thank you for any help you can give!
You can use row_number and self joins:
with cte as
(
select userid, stationid,
row_number() over(partition by userid order by stationid) rn
from tbl
)
select distinct c1.userid,
c1.stationid station_one,
c2.stationid station_two,
c3.stationid station_three
from cte c1
left join cte c2 on c1.userid=c2.userid and c2.rn=2
left join cte c3 on c1.userid=c3.userid and c3.rn=3
where c1.rn=1
See the demo
You can also do it with row_number and subqueries:
with cte as
(
select userid, stationid,
row_number() over(partition by userid order by stationid) rn
from tbl
)
select distinct userid,
(select stationid from cte c where c.userid=cte.userid and c.rn=1) station_one,
(select stationid from cte c where c.userid=cte.userid and c.rn=2) station_two,
(select stationid from cte c where c.userid=cte.userid and c.rn=3) station_three
from cte
See the demo
The easiest way to accomplish this in my experience is to use conditional aggregation:
WITH mydata AS (
SELECT 1 AS user_id, 203 AS station_id FROM dual
UNION ALL
SELECT 1 AS user_id, 204 AS station_id FROM dual
UNION ALL
SELECT 2 AS user_id, 203 AS station_id FROM dual
UNION ALL
SELECT 3 AS user_id, 487 AS station_id FROM dual
UNION ALL
SELECT 3 AS user_id, 3787 AS station_id FROM dual
UNION ALL
SELECT 3 AS user_id, 738 AS station_id FROM dual
)
SELECT user_id
, MAX(CASE WHEN rn = 1 THEN station_id END) AS station_one
, MAX(CASE WHEN rn = 2 THEN station_id END) AS station_two
, MAX(CASE WHEN rn = 3 THEN station_id END) AS station_three
FROM (
SELECT user_id, station_id, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY rownum ) AS rn
FROM mydata
) GROUP BY user_id;
Just replace the mydata CTE in the above query with whatever your table's name is:
SELECT user_id
, MAX(CASE WHEN rn = 1 THEN station_id END) AS station_one
, MAX(CASE WHEN rn = 2 THEN station_id END) AS station_two
, MAX(CASE WHEN rn = 3 THEN station_id END) AS station_three
FROM (
SELECT user_id, station_id, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY rownum ) AS rn
FROM mytable
) GROUP BY user_id;