我会尽力描述我的问题,但请不要犹豫,对问题发表评论。
想象一下需要在运行时使用EXEC sp_executesql
执行的动态查询。它将一个int列表作为输入,在该列表上执行WHERE
,并返回一个表,其中包含一列Ints作为输出。
然后我需要再次使用那些Ints
作为上一个查询的输入,n次。
我遇到的问题:
我不确定如何select into
来自动态查询的列表,将其作为输出接收,然后在同一查询中重新传递...我已准备好其余的递归,但这一位是让我疯了。任何帮助都将不胜感激 - 如果我需要包含我的数据库的一些基本表来证明,请告诉我。否则,如果有人能想出一个有效的简单表格,那么我将永远为你负债。
---更新---- 环境是MSSQLSERVER。例如:
包含3列的表:User1ID,User2ID,RelationID,其中每个用户通过关系映射到另一个用户。
问题:让所有满足潜在关系链的用户X时间。例如:
Dynamic query generated by .NET - SELECT URX.User2ID from UserRelations UR1
JOIN UR1.User2ID ON UR2.User1ID
JOIN UR2.User1ID ON UR3.User2ID
....
JOIN URX-1.User2ID ON URX.UserID1
WHERE RelationID = 1 OR RelationID = 2
AND OwnerID = @OwnerID
这将返回userIDs的单个表列。现在,如果我想再次运行它,我希望用户列表作为@OwnerID
的输入我希望能够多次这样做。
- 第二次编辑 -
tempTable评论似乎至少引导我走向了一些方向。谢谢你。我会尝试,并监视此线程以获得任何进一步的建议。干杯。
答案 0 :(得分:0)
我假设你正在使用SQL Server,因为这个问题用tsql(或通常是t-sql)标记,而sp_executeSQL
是SQL Server存储过程。鉴于这种假设,我认为你真的不需要求助sp_executeSQL
... SQL Server 2000增加了将局部变量定义为表类型的能力,这使得制作类似的东西变得更容易一些这个线程安全。
这里是一个脚本,它使用您描述的那种表创建数据库,向该表添加一些数据,创建存储过程以获取您想要的ID列表并将其作为逗号分隔列表返回在相同的varchar
输入参数中。 (显然这需要创建和删除数据库的权限。)存储过程参数@degrees
允许您控制要搜索值为0的其他相关用户的次数,使其搜索直到不再有找到。最后一个参数@relationid
,如果指定,将限制搜索到特定类型的关系,因此如果您只想查看祖先,可以指定@degrees = 0, @relationid = x
其中x是父关系的id它将返回父母的父母,直到找到该血统中的每个用户。
create database testme
go
use testme
go
/* this suppresses output of statement results
which can sometimes interfere with other code */
set nocount on
/* this just creates some dummy data to test with - modify it however you need */
create table UserRElations (id int identity primary key, user1id int, user2id int, relationid int)
insert into UserRelations (user1id, user2id, relationid) values (1,2, 1)
insert into UserRelations (user1id, user2id, relationid) values (1,3, 3)
insert into UserRelations (user1id, user2id, relationid) values (2,3, 8)
insert into UserRelations (user1id, user2id, relationid) values (3,5, 2)
insert into UserRelations (user1id, user2id, relationid) values (3,7, 17)
insert into UserRelations (user1id, user2id, relationid) values (4,7, 6)
insert into UserRelations (user1id, user2id, relationid) values (4,8, 1)
insert into UserRelations (user1id, user2id, relationid) values (5,10, 3)
insert into UserRelations (user1id, user2id, relationid) values (5,13, 1)
go
create procedure pGetFriendsOfFriends
@list nvarchar(1000) output, -- comma delimited list of int values from the user1id column of UserRelations
@degrees tinyint = 0, -- the number of times to search for more relations - 0 means no limit
@relationid int = null -- set this only if you want a specific type of relation
as
/* see above comment about nocount */
set nocount on
/* create a local variable that is a table
with one int column named id as the primary key */
declare @utbl table (id int primary key)
/* additional temp variables for later use */
declare @tmp nvarchar(5) -- if you have user1ids of more than 5 digits you might need this larger
declare @next int, @i int
set @list = @list + ',' -- make sure there's a comma after every id in the list
/* move the first item in the list to the table until the list is empty */
while (len(@list) > 0) begin
set @next = charindex(',', @list) -- find the first comma in the list
if (@next > 0) begin
/* we found a comma, insert an id into the temp table utbl */
set @tmp = ltrim(rtrim(left(@list, @next-1))) -- get a string up to the comma
if (isnumeric(@tmp) = 1) begin -- make sure the string is numeric
set @i = cast(@tmp as int) -- convert the string to an int
if (not exists (select id from @utbl where id = @i)) -- make sure we dont insert the same id twice
insert into @utbl values (@i) -- self explanatory
end
/* remove the id we just inserted into the table from the string */
set @list = substring(@list,@next+1,len(@list))
end else set @list = '' -- there weren't any more commas, we're done
end
/* now we're going to loop over the search for relations */
set @i = 1 -- set our temp variable to 1 to start the loop
while (@i <= @degrees -- loop from 1 to a specified number of @degrees of separation
or /* the keyword "exists" below will stop the select statement and return true as soon as a matching row is found */
(@degrees = 0 and exists
( -- !!NOTE!! this select statement must have the same where clause as below - otherwise you risk an infinite loop
select distinct urx.user2id
from UserRelations urx
where urx.user1id in (select id from @utbl)
and urx.user2id not in (select id from @utbl)
and (@relationid is null or urx.relationid = @relationid)
)
))
begin
/* add more relations for the current pass */
insert into @utbl
select distinct urx.user2id -- distinct ensures all the returned user2id values are unique
from UserRelations urx
where urx.user1id in (select id from @utbl) -- find relations for users already in the @utbl temp table
and urx.user2id not in (select id from @utbl) -- ignore any that are already in the @utbl temp table
and (@relationid is null or urx.relationid = @relationid) -- allow the caller to declare a specific relation type - null will return all types
set @i = @i + 1 -- !!NOTE!! if @degrees is not 0, the counter must be incremented (or you'll get an infinite loop)
end
set @list = '' -- we'll return the list parameter to the caller, but start with an empty string
/* create a cursor to loop over the ids we found */
declare cFoF cursor local fast_forward for
select id from @utbl
open cFoF -- open the cursor
fetch next from cFoF into @i -- get the first item from the cursor
while (@@FETCH_STATUS = 0) begin -- while there are more records in the cursor
set @list = ',' + cast(@i as nvarchar) + @list -- add the current item to the list
fetch next from cFoF into @i -- get the next item from the cursor
end
set @list = substring(@list, 2, len(@list)) -- remove the leading comma from the list
return -- make sure the @list parameter goes back to the caller
go
declare @list nvarchar(1000) = '1,2'
exec pGetFriendsOfFriends @list out, 2
print ''
print '@list=1,2, @depth=2 => @list=' + @list
set @list = '2'
exec pGetFriendsOfFriends @list out, 1
print ''
print '@list=2, @depth=1 => @list=' + @list
set @list = '1'
exec pGetFriendsOfFriends @list out, 0
print ''
print '@list=1, @depth=0 => @list=' + @list
set @list = '1'
exec pGetFriendsOfFriends @list out, 0, 1
print ''
print '@list=1, @depth=0, @relationid=1 => @list=' + @list
go
use master
go
drop database testme
go
当我执行此脚本时,我得到的输出如下:
@list=1,2, @depth=2 => @list=7,5,3,2,1
@list=2, @depth=1 => @list=3,2
@list=1, @depth=0 => @list=13,10,7,5,3,2,1
@list=1, @depth=0, @relationid=1 => @list=2,1
在大多数情况下,我个人发现将输入作为整数的字符串列表或以这种方式返回它实际上并不是必需的。如果将该本地表变量转换为SQL服务器type
(SQL Server 2008 or later),则可以将该类型的表作为参数传递给存储过程,或者将其返回一个,从而消除需要在int
和varchar
之间进行所有额外的字符串解析和转换,并且您的程序无疑会以这种方式执行得更快。