根据ID选择table1中的所有行以及table2中所有匹配的行

时间:2016-03-10 05:25:11

标签: sql-server

我有两张桌子

  1. UserInfo
  2. ContactInfo
  3. 我的表格结构如下:

    的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
    

    现在我正在寻找下面的结果

    结果

        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
    

    注意: UserInfoContactInfo之间的一对多关系

    任何人都可以帮助我完成它吗?非常感谢您的回复。

3 个答案:

答案 0 :(得分:2)

你的桌子结构不是很好。例如,如果您的用户有一封电子邮件但有两个手机号码怎么办?您与哪个手机号码关联?这种关联没有意义。这些都是当前形式的数据的准确表示:

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

相反,请考虑这样的结构:

ID | UserId | Type      | Mobile
1  | 1      | Mobile    | 111111
2  | 1      | Mobile    | 222222
3  | 1      | Email     | a@a.com

还有其他改进,但我离题了。要以当前状态回答您的问题,以下查询应该有效:

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

答案 1 :(得分:1)

如果我理解正确,并且您只想显示每个给定用户的最低ID的ContactInfo记录,我会执行以下操作:

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)

编辑: 使用OVER PARTITION BY通常也可以,但我通常更喜欢使用嵌套查询,因为它们通常有更好的执行计划。

答案 2 :(得分:1)

您可以执行dynamic crosstab来实现此目标。

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