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

SQL连接两个表并从第二个表中随机选择一个匹配的记录,sql,sql-server,Sql,Sql Server,我有两个表,一个用户表和一个MyPhotos表。UserTable包含一个UserID列和其他与用户信息相关的列。MyPhotos表包含UserID和ImageFile列 MyPhotos表可以为单个用户标识包含多条记录。我需要一种方法来查找特定用户的照片总数,以及为每个用户ID随机选择的一个图像文件 我当前使用的SQL语句每次都返回相同的图像,而不是随机图像。这是: SELECT MyPhotos.UserID, UsrTbl.ScreenName, COUNT(*) AS

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

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

我当前使用的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()
非常感谢您的帮助


谢谢

我通常喜欢在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和用户表连接起来以获得结果

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


我通常喜欢在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和用户表连接起来以获得结果

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

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
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, ...)
2) 如果我必须显示单个用户或少量用户,那么我将使用以下查询:

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
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
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
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, ...)
2) 如果我必须显示单个用户或少量用户,那么我将使用以下查询:

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
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

+1我使用您的DDL+插入脚本来测试我的解决方案。谢谢,很酷,非常感谢。我添加了selectuserid,天花(RAND()*MAX(ImgNo))作为ImgNo,COUNT(*)作为TotalPhotos,以获取用户提交的照片总数。完美的数据列表解决方案,该数据列表显示用户提交的全部照片以及该用户随机选择的图像。+1我使用您的DDL+插入脚本来测试我的解决方案。谢谢,很酷,非常感谢。我添加了selectuserid,天花(RAND()*MAX(ImgNo))作为ImgNo,COUNT(*)作为TotalPhotos,以获取用户提交的照片总数。数据列表显示用户提交的全部照片以及该用户随机选择的图像的完美解决方案?在一些记录中,它们可能是相同的。然而,在数千?为每行生成的16字节随机数-NEWID()与UserId。此外,我还可以将其替换为(选择0)并获得相同的结果。我只为每个用户ID生成1个随机数。值得思考的是…只是为了好玩,我将数据复制了2000次。我在子查询中使用了您的示例,newid()与我的示例相反。我看的是45毫秒和31毫秒。我想知道是否有人在博客上错了,哪种技术更好?你测试了哪种解决方案:1还是2?两者都使用子查询(派生表)。我认为这是我网站www.craftydba.com上一篇博客文章的一个很好的主题。我做了大约1小时的研究。对于每个帐户的大表(比如1000张图片),NEWID()可以在tempdb中排序。另一方面,我们都知道RAND()并不是真正随机的。我看到一些人使用RAND(校验和(NEWID()))。然而,我关于校验和vs hashbytes的文章在应用于160K行时显示了重复项。简言之,没有银弹。我认为NEWID()对于小数据是好的,但是对于大数据可能会执行不好。对于这种情况,我避免使用RAND(),因为它是一个。我想知道NEWID()ORDER BY相对于普通的旧INT如何执行?在一些记录中,它们可能是相同的。然而,在数千?为每行生成的16字节随机数-NEWID()与UserId。此外,我还可以将其替换为(选择0)并获得相同的结果。我只为每个用户ID生成1个随机数。值得思考的是…只是为了好玩,我将数据复制了2000次。我在子查询中使用了您的示例,newid()与我的示例相反。我看的是45毫秒和31毫秒。我想知道是否有人在博客上错了,哪种技术更好?你测试了哪种解决方案:1还是2?两者都使用子查询(派生表)。我认为这是我网站www.craftydba.com上一篇博客文章的一个很好的主题。我做了大约1小时的研究。对于每个帐户的大表(比如1000张图片),NEWID()可以在tempdb中排序。另一方面,我们都知道RAND()并不是真正随机的。我看到一些人使用RAND(校验和(NEWID()))。然而,我关于校验和vs hashbytes的文章在应用于160K行时显示了重复项。简言之,没有银弹。我认为NEWID()对于小数据是好的,但是对于大数据可能会表现不好。对于这种情况,我避免使用RAND(),因为它是一个。