2016-11-16 41 views
1

我有一个表UserPermission其中有多列TINYINT类型。例如读,写,更新,删除,访问等。SQL动态列和更新多列

我在存储过程中得到三个参数:@UserId, @ColNames, @ColValues其中@ColNames@ColValues是逗号分隔值。

如何使用传递的列名和相应的值插入或更新表行(如果已经存在)。

我试着编写对INSERT正常运行的动态查询,但我无法动态编写UPDATE查询,每列和它的值要连接起来。

任何反应,将不胜感激

在此先感谢。

+2

显示您的代码。 – Han

+1

样本数据和预期结果将会更有帮助。另外添加你到目前为止尝试的查询 –

+0

可能想要使用像'MERGE' – scsimon

回答

0

这是一种有点肮脏的方式来做你所需要的。但是,如果您创建以下存储过程:

CREATE FUNCTION [dbo].[stringSplit] 
(
    @String NVARCHAR(4000), 
    @Delimiter NCHAR(1) 
) 
RETURNS TABLE 
AS 
RETURN 
(
    WITH Split(stpos,endpos) 
    AS(
     SELECT 0 AS stpos, CHARINDEX(@Delimiter,@String) AS endpos 
     UNION ALL 
     SELECT endpos+1, CHARINDEX(@Delimiter,@String,endpos+1) 
      FROM Split 
      WHERE endpos > 0 
    ) 
    SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)), 
     'Data' = SUBSTRING(@String,stpos,COALESCE(NULLIF(endpos,0),LEN(@String)+1)-stpos) 
    FROM Split 
) 

然后,您可以使用该程序携起手来数据:

DECLARE @TotalCols INT 
DECLARE @TotalVals INT 

SET @TotalCols = (
     SELECT COUNT(ID) AS Total 
     FROM dbo.stringSplit('department, teamlead', ',') 
     ); 
SET @TotalVals = (
     SELECT COUNT(ID) AS Total 
     FROM dbo.stringSplit('IT, Bob', ',') 
     ); 

IF @TotalCols = @TotalVals 
BEGIN 
    IF OBJECT_ID('tempdb..#temptable') IS NOT NULL 
     DROP TABLE #temptable 

    CREATE TABLE #temptable (
     ColName VARCHAR(MAX) NULL 
     ,ColValue VARCHAR(MAX) NULL 
     ) 

    INSERT INTO #temptable 
    SELECT a.DATA 
     ,b.DATA 
    FROM dbo.stringSplit('department, teamlead', ',') AS a 
    INNER JOIN dbo.stringSplit('IT, Bob', ',') AS b ON a.Id = b.Id 

    SELECT * 
    FROM #temptable; 
END 

这不是很有效,但它会给你带来预期的效果。

然后,您可以使用临时表来根据需要更新,插入和删除。

+0

嗨cyclington,谢谢你的回应。但是它返回多行而不是单行,每个值都作为列名。如何将行转换为列 – Abdullah

0

而不必逗号分隔列表的我会创建一个单独的参数为每个列并使其默认值设置为NULL,并在代码更新什么,如果它的空或插入0。事情是这样的....

CREATE PROCEDURE usp_UserPermissions 
@UserID INT 
,@Update INT = NULL --<-- Make default values NULL 
,@Delete INT = NULL 
,@Read INT = NULL 
,@Write INT = NULL 
,@Access INT = NULL 
AS 
BEGIN 
    SET NOCOUNT ON; 

Declare @t TABLE (UserID INT, [Update] INT,[Read] INT 
       ,[Write] INT,[Delete] INT,[Access] INT) 

INSERT INTO @t (Userid, [Update],[Read],[Write],[Delete],[Access]) 
VALUES (@UserID , @Update , @Read, @Write , @Delete, @Access) 


IF EXISTS (SELECT 1 FROM UserPermission WHERE UserID = @UserID) 
    BEGIN 
     UPDATE up  -- Only update if a value was provided else update to itself 
     SET up.[Read] = ISNULL(t.[Read] , up.[Read]) 
      ,up.[Write] = ISNULL(t.[Write] , up.[Write]) 
      ,up.[Update] = ISNULL(t.[Update] , up.[Update]) 
      ,up.[Delete] = ISNULL(t.[Delete] , up.[Delete]) 
      ,up.[Access] = ISNULL(t.[Access] , up.[Access]) 
     FROM UserPermission up 
     INNER JOIN @t t ON up.UserID = t.UserID 
    END 
ELSE 
    BEGIN 
     -- if already no row exists for that User add a row 
     -- If no value was passed for a column add 0 as default 
    INSERT INTO UserPermission (Userid, [Update],[Read],[Write],[Delete],[Access]) 
    SELECT Userid 
     , ISNULL([Update], 0) 
     , ISNULL([Read], 0) 
     , ISNULL([Write], 0) 
     , ISNULL([Delete], 0) 
     , ISNULL([Access], 0) 
    FROM @t 
    END 

END 
+0

此方法的问题是,如果列允许NULL值,则无法区分NULL是否为预期值,或者只是意味着缺少一个值不打算更改。我试图找到一个答案,我和其他一些人在这个帖子上写道,当我发现。 – Matt

+0

@Matt因为这是一个权限表,我会想象一个用户有权限或不是,我们不想在权限表中放入一个未知的权限,毕竟它是应用程序的安全性,无论如何,你所要求的可以简单地在代码中通过删除最后一个插入中的ISNULL函数进行处理,其余部分仍然保持不变。谢谢 –

+0

如果列中不允许有空值,那么这个方法没有问题,如果不删除ISNULL和参数​​的DEFAULT值,那么这个方法就行得通,因为这个w不再是可选的。我发现这个问题深入到这种类型的结构:http://stackoverflow.com/questions/38952832/sql-updating-optional-parameters-php – Matt