SQL连接两个表并从第二个表中随机选择匹配的记录

时间:2014-01-04 18:21:03

标签: sql sql-server

我有两个表,一个用户表和一个MyPhotos表。 UserTable包含UserID列和与用户信息相关的其他列。 MyPhotos表包含UserID和ImageFile列。

MyPhotos表可以为单个UserID提供多条记录。我需要一种方法来查找特定用户的照片总数,以及为每个UserID选择 ONE 随机的图像文件。

我当前使用的SQL语句每次都返回相同的图像而不是随机图像。这是:

SELECT  MyPhotos.UserID, 
        UsrTbl.ScreenName, COUNT(*) AS TotalPhotos, 
        MAX(MyPhotos.ImagesFileName) AS Expr1
FROM MyPhotos 
INNER JOIN UsrTbl ON MyPhotos.UserID = UsrTbl.AccountID
GROUP BY MyPhotos.UserID, UsrTbl.ScreenName
ORDER BY NEWID()

非常感谢任何帮助!

谢谢!

2 个答案:

答案 0 :(得分:5)

我通常喜欢在tempdb中设置几个测试表,以便您可以使用该解决方案。我没有为设计添加任何完整性,因为你有真正的表格。

-- Just playing
use Tempdb;
Go

--
-- Table 1
--

-- Remove table
if OBJECT_ID('MyPhotos') > 0
drop table MyPhotos
go

-- Simple photo table
create table MyPhotos
(
UserID int,
ImagesFileName varchar(64)
);

-- Some data
insert into MyPhotos values
(1, 'c:\pics\fee.jpg'),
(1, 'c:\pics\fi.jpg'),
(1, 'c:\pics\foo.jpg'),
(1, 'c:\pics\fumb.jpg'),
(2, 'c:\pics\huff.jpg'),
(2, 'c:\pics\n.jpg'),
(2, 'c:\pics\puff.jpg');

-- Show the data
select * from MyPhotos


--
-- Table 2
--

-- Remove table
if OBJECT_ID('UsrTbl') > 0
drop table UsrTbl
go

-- Simple photo table
create table UsrTbl
(
AccountID int,
ScreenName varchar(64)
);

-- Some data
insert into UsrTbl values
(1, 'Jolly Green Giant'),
(2, 'Big Bad Wolf');

-- Show the data
select * from UsrTbl;

解决问题的一种方法是使用公用表表达式。

--
-- Grab a random pic by user id
-- 

;
WITH ctePhotos 
as
(
    SELECT 
      UserID, ImagesFileName, 
      ROW_NUMBER() OVER (PARTITION BY UserID ORDER BY UserID) as ImgNo
    FROM 
      MyPhotos 
),
cteRandomPick
AS
(
    SELECT UserID, CEILING(RAND() * MAX(ImgNo)) AS ImgNo
    FROM ctePhotos
    GROUP BY UserId
)
SELECT 
  p.UserId,
  u.ScreenName,
  p.ImgNo,
  p.ImagesFileName
FROM UsrTbl as u INNER JOIN ctePhotos as p ON u.AccountID = p.UserID
INNER JOIN cteRandomPick as r ON p.UserID = r.UserID and p.ImgNo = r.ImgNo;

ctePhones只是按用户ID枚举图片,图片编号。 cteRandomPick抓住了最大值 图像编号并将其乘以RAND()函数以获得随机图像。

最后但并非最不重要的是,身体连接两个CTE和用户表以获得结果。

如果你多次运行代码,你会得到不同的选择。

enter image description here

答案 1 :(得分:1)

1)如果我必须显示所有用户,那么我将使用以下查询:

SELECT  u.AccountID, u.ScreenName, oa.RandomImagesFileName
FROM    dbo.UsrTbl u
LEFT JOIN (
    SELECT  p.UserID, p.ImagesFileName AS RandomImagesFileName,
            ROW_NUMBER() OVER(PARTITION BY p.UserID ORDER BY NEWID()) AS RowNum
    FROM    dbo.MyPhotos p
) oa ON u.AccountID = oa.UserID
WHERE oa.RowNum = 1

2)如果我必须显示单个用户或少数用户,那么我将使用以下查询:

SELECT  u.AccountID, u.ScreenName, oa.RandomImagesFileName
FROM    dbo.UsrTbl u
OUTER APPLY (
    SELECT  TOP(1) p.ImagesFileName AS RandomImagesFileName
    FROM    dbo.MyPhotos p -- Uncomment if execution plan includes a Scan; This WITH(INDEX=IX_MyPhotos_UserID_#_ImagesFileName) or WITH(FORCESEEK) table hints should "forces" DBMS to select an Index Seek instead of Scan
    WHERE   p.UserID = u.AccountID
    ORDER BY NEWID()
) oa
WHERE u.AccountID IN (1, ...)

3)以下索引应该/可以帮助两个查询:

CREATE INDEX IX_MyPhotos_UserID_#_ImagesFileName
ON dbo.MyPhotos (UserID)
INCLUDE (ImagesFileName);
GO