How to marge data from the table using sql server? - sql-server

Hi i am create the one store procedure for the get data and send push notification.and i want to marge this data and get in to one raw. but how can do that i don't know. i need help please help me let me know how can do that.this is my query here below i have write :
This is query =>
SELECT N.NotificationId,
N.UserId,
N.ActionUserId,
(CASE WHEN N.NotificationTypeId = 1 THEN 1
WHEN N.NotificationTypeId = 7 THEN 3
ELSE
2
END) AS TypeId,
AU.ProfileImage,
AU.UserName,
N.IsRead,
(CASE WHEN N.NotificationTypeId = 1 THEN 1
WHEN N.NotificationTypeId = 7 THEN 3
ELSE
2
END) AS TypeId,
N.NotificationTypeId,
N.InsertDateTime
FROM Notifications N
INNER JOIN Users U ON N.UserId = U.UserId
INNER JOIN Users AU ON N.ActionUserId = AU.UserId
ORDER BY N.InsertDateTime DESC
This is my current o/p =>
NotificationId | UserId | ActionUserId | UserName | NotificationTypeId | InsertDateTime | ProfileImage
6 20 15 hbc 1 2017-06-22 17:14:16.803 20170416032403869.jpeg
5 20 16 tyu 1 2017-06-22 17:12:12.297 20170416031522534.jpeg
4 20 17 opl 1 2017-06-22 17:11:58.060 20170416031250102.jpeg
3 10 11 abc 1 2017-06-22 16:14:16.803 20170416032403867.jpeg
2 10 12 xyz 1 2017-06-22 16:14:12.297 20170416031522533.jpeg
1 10 13 rty 1 2017-06-22 16:13:58.060 20170416031250101.jpeg
This is my expected o/p =>
NotificationId | UserId | ActionUserId | UserName | NotificationTypeId | InsertDateTime | ProfileImage | NotificationText
6 20 15 hbc 1 2017-06-22 17:14:16.803 20170416032403869.jpeg hbc,tyu and 1 other users followed you
3 10 11 abc 1 2017-06-22 16:14:16.803 20170416032403867.jpeg abc,xyz and 1 other users followed you
i want to like this marge this data any one know how can do that please let me know.

You can do this with a derived table and some windowed functions. I have also added in a bit of logic to make sure the Notification Text has the correct English depending on the number of other users included:
-- Create test data
declare #Notifications table(NotificationID int, UserID int, ActionUserID int, NotificationTypeID int, InsertDateTime datetime);
declare #Users table(UserID int, UserName nvarchar(10), ProfileImage nvarchar(50))
insert into #Notifications values (6,20,15,1,'2017-06-22 17:14:16.803'),(5,20,16,1,'2017-06-22 17:12:12.297'),(4,20,17,1,'2017-06-22 17:11:58.060'),(3,10,11,1,'2017-06-22 16:14:16.803'),(2,10,12,1,'2017-06-22 16:14:12.297'),(1,10,13,1,'2017-06-22 16:13:58.060');
insert into #Users values (15,'hbc','20170416032403869.jpeg'),(16,'tyu','20170416031522534.jpeg'),(17,'opl','20170416031250102.jpeg'),(10,'aaa',''),(11,'abc','20170416032403867.jpeg'),(12,'xyz','20170416031522533.jpeg'),(13,'rty','20170416031250101.jpeg');
-- Specify UserID
declare #UserID int = 10;
-- Create Notification
with d as
(
select n.NotificationID
,n.UserID
,n.ActionUserID
,au.UserName
,n.NotificationTypeID
,n.InsertDateTime
,au.ProfileImage
,row_number() over (partition by n.UserID order by n.InsertDateTime desc) as rn
,count(*) over (partition by n.UserID) as c
from #Notifications n
join #Users au
on(n.ActionUserID = au.UserID)
)
select d.NotificationID
,d.UserID
,d.ActionUserID
,d.UserName
,d.NotificationTypeID
,d.InsertDateTime
,d.ProfileImage
,d.UserName
+ isnull(case when d2.c = 2
then ' and '
else ', '
end
+ d2.UserName
,'')
+ case when d2.c > 2
then ' and ' + cast(d2.c-2 as nvarchar(10)) + ' other users'
else ''
end
+ ' followed you' as NotificationText
from d
left join d as d2
on(d.UserID = d2.UserID
and d2.rn = 2
)
where d.rn = 1;
Output:
+----------------+--------+--------------+----------+--------------------+-------------------------+------------------------+-----------------------------------------+
| NotificationID | UserID | ActionUserID | UserName | NotificationTypeID | InsertDateTime | ProfileImage | NotificationText |
+----------------+--------+--------------+----------+--------------------+-------------------------+------------------------+-----------------------------------------+
| 3 | 10 | 11 | abc | 1 | 2017-06-22 16:14:16.803 | 20170416032403867.jpeg | abc, xyz and 1 other users followed you |
| 6 | 20 | 15 | hbc | 1 | 2017-06-22 17:14:16.803 | 20170416032403869.jpeg | hbc, tyu and 1 other users followed you |
+----------------+--------+--------------+----------+--------------------+-------------------------+------------------------+-----------------------------------------+

You can try this:
SELECT Q.NotificationId,
Q.UserId,
Q.ActionUserId,
(CASE WHEN Q.NotificationTypeId = 1 THEN 1
WHEN Q.NotificationTypeId = 7 THEN 3
ELSE
2
END) AS TypeId,
Q.ProfileImage,
Q.UserName,
Q.IsRead,
(CASE WHEN Q.NotificationTypeId = 1 THEN 1
WHEN Q.NotificationTypeId = 7 THEN 3
ELSE
2
END) AS TypeId2,
Q.NotificationTypeId,
Q.InsertDateTime
FROM ( SELECT N.UserId, N.ActionUserId, N.NotificationTypeId,
AU.ProfileImage, AU.UserName, N.IsRead, N.InsertDateTime, N.NotificationID
FROM Notifications N
INNER JOIN Users U ON N.UserId = U.UserId
INNER JOIN Users AU ON N.ActionUserId = AU.UserId
WHERE N.UserId = #UserId ) Q
INNER JOIN (SELECT MAX(NotificationID) AS MaxNotifID, UserID FROM
dbo.Notifications
WHERE UserID = #userID GROUP BY UserID ) R ON
Q.NotificationID = R.MaxNotifID AND Q.UserID = R.USerID
ORDER BY Q.InsertDateTime DESC

Related

Using array and loop to update MySQL table

I have a table with 3 columns
pID | key_name | value
-----------------------
10 | 'series' |'Songs'
10 | 'wood' |'Beech'
10 | 'language' |'German'
11 | 'series' |'Songs'
11 | 'wood' |'Oak'
11 | 'language' |'French'
12 | 'series' |'Exams'
12 | 'language' |'English'
I need to update a table where the key_names are now column names, thus
pID | series | wood | language
-----------------------------
10 ! 'Songs'|'Beech'|'German'
11 | 'Songs'|'Oak' |'French'
12 | 'Exams'| |'English'
Now I could write some SQL like
UPDATE dest-tbl INNER JOIN start-tbl
ON dest-tbl.pID = start-tbl.pID
SET dest-tbl.series = start-tbl.value
WHERE dest-tbl.key_name = 'series'
but since there are 65 different key_name values that would mean having to have 65 variations on that SQL.
It strikes me that the best way to do that might be to create an array of the key_name values and loop through that, except that I haven't go a clue how to do that.
Can anyone help me with this?
Using MariaDB v10.3
MTIA
EDIT:
I think I'm close to an answer with the SQL below.
I do need to insert this into another table and to filter the results based on the value of a field in another table. The code from SELECT down to GROUP creates the output I need but I've now got a problem with the JOIN part
INSERT INTO results ('series', 'wood', 'language')
SELECT table1.pID,
MAX(CASE WHEN table1.meta_key = 'series' THEN table1.meta_value END) 'series',
MAX(CASE WHEN table1.meta_key = 'wood' THEN table1.meta_value END) 'wood',
MAX(CASE WHEN table1.meta_key = 'language' THEN table1.meta_value END) 'language'
FROM table1
GROUP BY table1.pID
INNER JOIN ON table2.id = table1.pID
WHERE table2.id.type = 'product';
SELECT
t1.pID,
t1.value,
t2.value,
t3.value
FROM Table1 t1
LEFT JOIN Table1 t2 on t2.pID=t1.pID and t2.key_name='wood'
LEFT JOIN Table1 t3 on t3.pID=t1.pID and t3.key_name='language'
WHERE t1.key_name='series';
output:
+ -------- + ---------- + ---------- + ---------- +
| 10 | Songs | Beech | German |
| 11 | Songs | Oak | French |
| 12 | Exams | | English |
+ -------- + ---------- + ---------- + ---------- +
DBFIDDLE
As i said in the comments to get a flexible solution you need dynamic sql
CREATE TABLE tab1 (
`pID` INTEGER,
`key_name` VARCHAR(10),
`value` VARCHAR(9)
);
INSERT INTO tab1
(`pID`, `key_name`, `value`)
VALUES
('10', 'series', 'Songs'),
('10', 'wood', 'Beech'),
('10', 'language', 'German'),
('11', 'series', 'Songs'),
('11', 'wood', 'Oak'),
('11', 'language', 'French'),
('12', 'series', 'Exams'),
('12', 'language', 'English');
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(CASE WHEN `key_name` = ''',
`key_name`,
''' THEN `value` ELSe "" END) AS `',
`key_name`, '`'
)
) INTO #sql
FROM `tab1` ;
SET #sql = CONCAT('SELECT `pID`, ',#sql,' from tab1
GROUP BY `pID`');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
✓
✓
pID | language | series | wood
--: | :------- | :----- | :----
10 | German | Songs | Beech
11 | French | Songs | Oak
12 | English | Exams |
✓
db<>fiddle here
My solution, tested and working though a bit slow, is as follows:-
INSERT INTO results ('series', 'wood', 'language')
SELECT table1.pID,
MAX(CASE WHEN table1.meta_key = 'series' THEN table1.meta_value END) 'series',
MAX(CASE WHEN table1.meta_key = 'wood' THEN table1.meta_value END) 'wood',
MAX(CASE WHEN table1.meta_key = 'language' THEN table1.meta_value END) 'language'
FROM table1, table2
WHERE table2.ID=table1_pID AND table2.id.type = 'product';
GROUP BY table1.pID

SQL - How to combine rows

I have the following table which looks at calls and attendances. I got this by using union all on a 'calls' and 'attendances' tables and then used row number on the ID and ordered by dates.
Table1:
Type | ID | Call/AttendanceDate | RowNum
------------|----|---------------------|--------
Attendance | 12 | 2018-09-16 10:11:00 | 82
Call | 12 | 2018-09-18 14:11:47 | 83
Call | 12 | 2018-10-02 17:26:13 | 84
Call | 12 | 2018-10-05 14:58:31 | 85
Attendance | 12 | 2018-10-13 01:41:00 | 86
Call | 12 | 2018-10-13 02:39:12 | 87
Call | 12 | 2018-10-13 04:31:22 | 88
Attendance | 12 | 2018-10-13 14:29:00 | 89
Call | 12 | 2018-10-13 14:59:19 | 90
Attendance | 12 | 2018-10-15 15:50:00 | 91
The code I used for this is:
WITH CTE1 AS
(
SELECT 'Call' as [Type], ID, CallDate AS Date1
FROM CallsTable
UNION ALL
SELECT 'Attendance' as [Type], ID, AttendanceDate AS Date2
FROM AttendanceTable]
)
,CTE2 AS
(
SELECT [Type], Date1, ID, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Date1 ASC) AS RowNum
FROM CTE1
)
--------------------------------OUTPUT--------------------------------
SELECT a.[Type], a.ID, a.Date1, a.RowNum
FROM CTE2 a
JOIN CTE2 b
ON a.ID= b.ID
AND a.RowNum = b.RowNum + 1
WHERE a.ID = '12'
ORDER BY ID, RowNum
I want to modify this to look like the below output, so that whenever an attendance follows a call, it should be in the same row.
Table2:
Type | ID | CallDate | RowNum | Type | AttendanceDate | RowNum
------|----|------------------|--------|------------|------------------|--------
NULL | 12 | NULL | NULL | Attendance | 16/09/2018 10:11 | 82
Call | 12 | 18/09/2018 14:11 | 83 | NULL | NULL | NULL
Call | 12 | 02/10/2018 17:26 | 84 | NULL | NULL | NULL
Call | 12 | 05/10/2018 14:58 | 85 | Attendance | 13/10/2018 01:41 | 86
Call | 12 | 13/10/2018 02:39 | 87 | NULL | NULL | NULL
Call | 12 | 13/10/2018 04:31 | 88 | Attendance | 13/10/2018 14:29 | 89
Call | 12 | 13/10/2018 14:59 | 90 | Attendance | 15/10/2018 15:50 | 91
Is this possible? What code could I use?
Use FULL JOIN
SELECT
*
FROM
(SELECT * FROM CTE2 WHERE Type = 'CALL') A
FULL JOIN
(SELECT * FROM CTE2 WHERE Type = 'ATTENDANCE') B
ON A.ID = B.ID AND A.RowNum = B.RowNum - 1
You can use APPLY :
SELECT C.[Type], C.ID, C.CallDate, C.RowNum,
(CASE WHEN C2.RowNum - C.RowNum = 1 THEN C2.[TYPE] end) [TYPE],
(CASE WHEN C2.RowNum - C.RowNum = 1 THEN C2.CallDate end) AttendanceDate,
(CASE WHEN C2.RowNum - C.RowNum = 1 THEN C2.RowNum end) RowNum
FROM CTE2 C OUTER APPLY
(SELECT TOP (1) C2.*
FROM CTE2 C2
WHERE C2.ID = C.ID AND C2.[Type] = 'Attendance' AND C2.RowNum > C.RowNum
ORDER BY C2.RowNum
) C2
WHERE C.ID = 12 AND C.[Type] = 'Call';
Not as elegant, but works for me, a table valued function
alter FUNCTION GetCallActivity()
RETURNS #activityTable TABLE
(
call_type varchar(16),
call_id int,
call_date datetime,
call_rownum int,
atnd_type varchar(16),
atnd_id int,
atnd_date datetime,
atnd_rownum int
)
AS
BEGIN
-- initialize the return table
insert into #activityTable
(call_type, call_id, call_date, call_rownum )
select a.type, a.id, a.activity_date, a.rownum
from stack_calls a
where a.type = 'Call'
order by a.activity_date;
-- match to the attendence recs to the call recs
update #activityTable
set atnd_type = b.type,
atnd_id = b.id,
atnd_date = b.activity_date,
atnd_rownum = b.rownum
from stack_calls b
join #activityTable a
on b.rownum = a.call_rownum + 1
where b.type = 'Attendance';
-- deal with the edge cases
insert into #activityTable
( atnd_type, atnd_id, atnd_date, atnd_rownum )
select x.type,
x.id,
x.activity_date,
x.rownum
from
(
select a.type,
a.id,
a.activity_date,
a.rownum,
lag(a.type, 1) over (order by a.activity_date) as prev_type
from stack_calls a
where a.type = 'Attendance'
) x
where x.prev_type is null
RETURN
END
GO

Sql rows count with grouping

Hello I have two tables as below
tblContactType
typeId typeName active
1 Email 1
2 Phone 1
3 Address 1
4 Fax 1
tblContact
id IdName typeId groupId
100 test 1 1
101 test2 1 1
102 test3 1 2
103 test4 2 2
104 test5 2 3
105 test6 3 3
Want the results to be with column names as typeName count and grouped by group id. Results should be total number of types associated to a group,which are associated to a contact.
GroupId EmailCount PhoneCount AddressCount FaxCount
1 2 0 0 0
2 1 1 0 0
3 0 1 1 0
You can group by and pivot as below:
Select * from (
Select t.groupid, tct.typename, t.id from tblContact t
inner join tblContactType tct
on t.typeid = tct.typeid
) a
pivot (count(a.id) for typename in ([Email],[Phone],[Address],[Fax]) ) p
For dynamic list of columns you can use dynamic query as below:
declare #cols1 varchar(max)
declare #query nvarchar(max)
Select #cols1 = stuff((Select distinct ','+QuoteName(typename) from tblContactType for xml path('')),1,1,'')
Set #query = ' Select * from (
Select t.groupid, tct.typename, t.id from tblContact t
inner join tblContactType tct
on t.typeid = tct.typeid
) a
pivot (count(a.id) for typename in (' + #cols1 + ') ) p '
Select #query --Check the generated query is good and then execute below
--exec sp_executesql #query
Output as below:
+---------+---------+-------+-----+-------+
| groupid | Address | Email | Fax | Phone |
+---------+---------+-------+-----+-------+
| 1 | 0 | 2 | 0 | 0 |
| 2 | 0 | 1 | 0 | 1 |
| 3 | 1 | 0 | 0 | 1 |
+---------+---------+-------+-----+-------+
Here is another solution.
SELECT groupId,
SUM(CASE WHEN c.typeId = 1 THEN 1 ELSE 0 END) 'EmailCount',
SUM(CASE WHEN c.typeId = 2 THEN 1 ELSE 0 END) 'PhoneCount',
SUM(CASE WHEN c.typeId = 3 THEN 1 ELSE 0 END) 'AddressCount',
SUM(CASE WHEN c.typeId = 4 THEN 1 ELSE 0 END) 'FaxCount'
FROM tblContact c
JOIN tblContactType ct ON c.typeId = ct.typeId
GROUP BY groupId
Results
-------------------------------------------------------------
groupId | EmailCount | PhoneCount | AddressCount | FaxCount
-------------------------------------------------------------
1 | 2 | 0 | 0 | 0
2 | 1 | 1 | 0 | 0
3 | 0 | 1 | 1 | 0
-------------------------------------------------------------

SQL Pivot table with string fields

I am new with SQL Pivot, so i would like to have a detail explaination regarding to it. My table is like the following:
PID | NAME | PlateNumber | COUNTRY|
------------------------------------------
111 | Alex | ab123456 | GB |
------------------------------------------
111 | Alex | fe123344 | ES |
------------------------------------------
111 | Alex | r223456e | US |
------------------------------------------
112 | Simon | t22er563 | GB |
------------------------------------------
112 | Simon | q32345ke | DE |
------------------------------------------
113 | Ben | ve923456 | IT |
------------------------------------------
And i would to have the result in the following format:
PID |NAME |PlateNumber1|PlateNumber2| PlateNumber3|COUNTRY1| COUNTRY2| COUNTRY3|
--------------------------------------------------------------------------------
111 | Alex | ab123456 | fe123344 | r223456e | GB | ES | US |
--------------------------------------------------------------------------------
112 | Simon | t22er563| q32345ke | | GB | DE | |
--------------------------------------------------------------------------------
113 | Ben | ve923456| | | IT | | |
--------------------------------------------------------------------------------
Could you please help me with this?
Thank you in advance!
The traditional cross tab / conditional aggregation version would be like so:
test setup: http://rextester.com/SKMUL25726
select
pid
, name
, PlateNumber1 = max(case when rn = 1 then PlateNumber end)
, PlateNumber2 = max(case when rn = 2 then PlateNumber end)
, PlateNumber3 = max(case when rn = 3 then PlateNumber end)
, Country1 = max(case when rn = 1 then Country end)
, Country2 = max(case when rn = 2 then Country end)
, Country3 = max(case when rn = 3 then Country end)
from (
select t.*
, rn=row_number() over (partition by PID order by platenumber)
from t
) as t
group by pid, name
returns:
+-----+-------+--------------+--------------+--------------+----------+----------+----------+
| pid | name | PlateNumber1 | PlateNumber2 | PlateNumber3 | Country1 | Country2 | Country3 |
+-----+-------+--------------+--------------+--------------+----------+----------+----------+
| 111 | Alex | ab123456 | fe123344 | r223456e | GB | ES | US |
| 113 | Ben | ve923456 | NULL | NULL | IT | NULL | NULL |
| 112 | Simon | q32345ke | t22er563 | NULL | DE | GB | NULL |
+-----+-------+--------------+--------------+--------------+----------+----------+----------+
Using PIVOT:
with your_table(PID , NAME , PlateNumber , Country) as (
select 111 , 'Alex' , 'ab123456' , 'GB' union all
select 111 , 'Alex' , 'fe123344' , 'ES' union all
select 111 , 'Alex' , 'r223456e' , 'US' union all
select 112 , 'Simon' , 't22er563' , 'GB' union all
select 112 , 'Simon' , 'q32345ke' , 'DE' union all
select 113 , 'Ben' , 've923456' , 'IT'
)
select pid,
name,
max(PlateNumber1) PlateNumber1,
max(PlateNumber2) PlateNumber2,
max(PlateNumber3) PlateNumber3,
max(Country1) Country1,
max(Country2) Country2,
max(Country3) Country3
from (
select *,
'Country' + cast(row_number() over (
partition by PID order by pnum
) as varchar(30)) cn
from (
select t.*,PlateNumber pnum,
'PlateNumber' + cast(row_number() over (
partition by PID order by PlateNumber
) as varchar(30)) pn
from your_table t
) t
pivot(max(PlateNumber) for pn in ([PlateNumber1], [PlateNumber2], [PlateNumber3])) x
) t
pivot(max(Country) for cn in ([Country1], [Country2], [Country3])) x
group by pid,
name;
Demo
If the PlateNumber count is not fixed of PID, you can use dynamic statement.
CREATE TABLE #tt(PID INT,[Name] VARCHAR(10),PlateNumber VARCHAR(10),Country VARCHAR(5))
INSERT INTO #tt
select 111 , 'Alex' , 'ab123456' , 'GB' union all
select 111 , 'Alex' , 'fe123344' , 'ES' union all
select 111 , 'Alex' , 'r223456e' , 'US' union all
select 112 , 'Simon' , 't22er563' , 'GB' union all
select 112 , 'Simon' , 'q32345ke' , 'DE' union all
select 113 , 'Ben' , 've923456' , 'IT'
DECLARE #SQL VARCHAR(max),#col VARCHAR(max)
---- Get the max line count of all the PID
DECLARE #MaxNumber INT =0
SELECT #MaxNumber=CASE WHEN COUNT(0)>#MaxNumber THEN count(0) ELSE #MaxNumber END FROM #tt AS t GROUP BY t.Name,t.PID
PRINT #MaxNumber
SELECT #col=ISNULL(#col+',','')+'PlateNumber'+LTRIM(sv.number)+',Country'+LTRIM(sv.number)
FROM master.dbo.spt_values AS sv WHERE sv.type='P' AND sv.number BETWEEN 1 AND #MaxNumber
PRINT #col
SET #SQL='
SELECT * FROM (
SELECT pid,[name],c.col_value,c.col_title+LTRIM(ROW_NUMBER()OVER(PARTITION BY t.PID,[name],c.col_title ORDER BY (SELECT 0))) AS col_title FROM #tt AS t
CROSS APPLY(VALUES(''PlateNumber'',t.PlateNumber),(''Country'',t.Country))c(col_title,col_value)
) AS t
PIVOT(MAX(col_value) FOR col_title IN ('+#col+')) p'
EXEC(#SQL)
pid name PlateNumber1 Country1 PlateNumber2 Country2 PlateNumber3 Country3
----------- ---------- ------------ ---------- ------------ ---------- ------------ ----------
111 Alex r223456e GB fe123344 ES ab123456 US
113 Ben ve923456 IT NULL NULL NULL NULL
112 Simon q32345ke DE t22er563 GB NULL NULL

Select all rows in table1 and all matched rows in table2 in same row based on ID

I have two tables
UserInfo
ContactInfo
My table structure is as below:
UserInfo
UserId Name
1 Sandeep
2 James
3 Vishal
ContactInfo
Id UserId Email Mobile
1 1 e1#somemail.com 111111
2 1 e2#somemail.com 222222
3 3 v1#somemail.com 010101
Now i'm looking for result like below
Result
UserId UserName Email1 Mobile1 Email2 Mobile2
1 Sandeep e1#somemail.com 111111 e2#somemail.com 222222
2 James NULL NULL NULL NULL
3 Vishal v1#somemail.com 010101 NULL NULL
Note: One to many relationship between UserInfo and ContactInfo
Can anyone help me to get it done? Your response are highly appreciated.
Your tables aren't really structured very well. For example, what if your user has one email but two mobile numbers? Which mobile number are you associating with the email? This association has no meaning. These are all accurate representations of this data in its current form:
1.
ID | UserId | Email | Mobile
1 | 1 | a#a.com | 111111
2 | 1 | NULL | 222222
2.
ID | UserId | Email | Mobile
1 | 1 | NULL | 111111
2 | 1 | a#a.com | 222222
3.
ID | UserId | Email | Mobile
1 | 1 | NULL | 111111
2 | 1 | NULL | 222222
3 | 1 | a#a.com | NULL
Instead, consider a structure like this:
ID | UserId | Type | Mobile
1 | 1 | Mobile | 111111
2 | 1 | Mobile | 222222
3 | 1 | Email | a#a.com
There are other improvements to make as well, but I digress. To answer your question in its current state, the following query should work:
SELECT U.Name,
CI1.Email as Email1,
CI1.Mobile as Mobile1,
CI2.Email as Email2,
CI2.Mobile as Mobile2
FROM UserInfo U
LEFT JOIN (
SELECT UserID,
Email,
Mobile,
ROW_NUMBER() OVER (PARTITION BY UserID ORDER BY ID) as row
FROM ContactInfo
) CI1
ON U.UserID = CI1.UserID
AND CI1.row = 1
LEFT JOIN (
SELECT UserID,
Email,
Mobile,
ROW_NUMBER() OVER (PARTITION BY UserID ORDER BY ID) as row
FROM ContactInfo
) CI2
ON U.UserID = CI1.UserID
AND CI1.row = 2
You can do a dynamic crosstab to achieve this.
DECLARE #sql NVARCHAR(MAX)
SELECT #sql =
'SELECT
u.UserId
, u.Name' + CHAR(10)
SELECT #sql = #sql +
' , MAX(CASE WHEN t.rn = ' + CONVERT(VARCHAR(10), rn) +' THEN t.Email END) AS '
+ QUOTENAME('Email' + CONVERT(VARCHAR(10), rn)) + CHAR(10) +
' , MAX(CASE WHEN t.rn = ' + CONVERT(VARCHAR(10), rn) +' THEN t.Mobile END) AS '
+ QUOTENAME('Mobile' + CONVERT(VARCHAR(10), rn)) + CHAR(10)
FROM (
SELECT DISTINCT
rn = ROW_NUMBER() OVER(PARTITION BY UserId ORDER BY Id)
FROM ContactInfo
)t
-- Add filter here for ROW_NUMBER, e.g. starting from the 25th row to 50th
WHERE rn BETWEEN 25 AND 50
SELECT #sql = #sql +
'FROM UserInfo u
LEFT JOIN (
SELECT *,
rn = ROW_NUMBER() OVER(PARTITION BY UserId ORDER BY Id)
FROM ContactInfo
)t
ON t.UserId = u.UserId
-- Add filter here for ROW_NUMBER, e.g. starting from the 25th row to 50th
AND rn BETWEEN 25 AND 50
GROUP BY u.UserId, u.Name
ORDER BY u.UserId, u.Name'
PRINT #sql
EXEC(#sql)
DEMO
If I understand you correctly, and you simply want to display the ContactInfo record with the lowest Id with each given user, I'd do something like the following:
SELECT ci.UserId, ui.Name AS UserName, ci.*
FROM UserInfo ui
INNER JOIN (
SELECT ci.UserId AS UserId, MIN(ci.Id) AS Id
FROM ContactInfo ci
GROUP BY ci.UserId
) AS q ON (q.UserId = ui.UserId)
INNER JOIN ContactInfo ci ON (ci.Id = q.Id)
EDIT:
Using OVER PARTITION BY often works as well, but I typically prefer to use nested queries as they usually have better execution plans.

Resources