繁体   English   中英

如何使用SQL中存储过程的值填充结果视图列?

[英]How do I Populate a Result View Column with Value from Stored Proc in SQL?

编辑:是一个存储过程,而不是一个功能 ,对不起!

我有一张桌子:

TABLE: dbo.Numbers 
Number_ID | Number
1         | 0
2         | 1
3         | 2
4         | 3
5         | 4
6         | 5
7         | 6     
8         | 7

我想要以下输出( 作为视图 ):

Number_ID | ModifiedNumber
1         | lol the num is 0
2         | lol the num is 1
3         | lol the num is 2
4         | lol the num is 3
5         | lol the num is 4
6         | lol the num is 5
7         | lol the num is 6      
8         | lol the num is 7

我有一个存储过程来执行此操作:

CREATE PROCEDURE dbo.UselessStoredProc @inputNum int
AS
SELECT 'lol my number is: ' + CONVERT(varchar(max), @inputNum)
GO

--TEST
EXEC dbo.UselessStoredProc @inputNum=2;

我的最终目标是通过存储过程填充ModifiedNumber col,例如:

SELECT Number_ID, EXEC UselessStoredProc @inputNum = Number_ID  as ModifiedNumber
FROM [TestDb].[dbo].[Numbers]

显然这不起作用。 我该如何做到这一点。

PS请不要告诉我“只是这样做:

SELECT Number_ID, 'lol my number is: ' + CONVERT(varchar(max), Number_ID) as ModifiedNumber
FROM [TestDb].[dbo].[Numbers]

我很清楚我能做到这一点 - 这显然是一个例子,真正的代码要复杂得多,需要一个存储过程 注意,我故意从样本存储过程中返回一个字符串 - 我需要复杂的值,而不仅仅是int

编辑:

SQL Server 2012

功能不是一种选择。 我为这种困惑道歉。 我的“函数”需要调用内置的存储过程,函数不能这样做。

编辑2:我需要SP而不是函数的原因是因为我需要动态查找某些表的主键,所以我需要使用动态SQL。 我认为这是太多的信息,但看起来有必要:

DECLARE @sql nvarchar(max) = 'SELECT @Data_Table_Key = COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + ''.'' + QUOTENAME(CONSTRAINT_NAME)), ''IsPrimaryKey'') = 1 AND TABLE_NAME = ''' + @Data_Table_Name + ''''
EXECUTE sp_executesql @sql, N'@Data_Table_Key varchar(200) OUTPUT', @Data_Table_Key OUTPUT

编辑:最后,我试图做一些无法用openrowset做的事情 ,这是一个肮脏,肮脏的黑客。 我最后写了一个存储过程,每5分钟写一个表,第三方软件使用该表。 我感谢所有的帮助。 我接受了最能帮助我的答案,但是循环答案也很有帮助,并得到了我需要的东西,尽管表现令人无法接受。

基于

编辑2:我需要SP而不是函数的原因是因为我需要动态查找某些表的主键,所以我需要使用动态SQL。 我认为这是太多的信息,但看起来有必要:

DECLARE @sql nvarchar(max) = 'SELECT @Data_Table_Key = COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + ''.'' + QUOTENAME(CONSTRAINT_NAME)), ''IsPrimaryKey'') = 1 AND TABLE_NAME = ''' + @Data_Table_Name + ''''
EXECUTE sp_executesql @sql, N'@Data_Table_Key varchar(200) OUTPUT', @Data_Table_Key OUTPUT

我糊涂了。 为什么要在动态SQL中首先执行此操作。

你也可以这样做:

SELECT @Data_Table_Key = COLUMN_NAME 
  FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
 WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1 
  AND TABLE_NAME = @Data_Table_Name 

并在@Data_Table_Key变量中得到相同的结果。

也就是说,只要您尝试从主键中包含多个字段的表中获取PK列,您的方法就会给您“不完整”的结果。 不知道你会得到哪一个,但它只是其中之一(通常是最后一个,但你不能指望它!)。

一些示例代码:

CREATE TABLE t_test_multiple_pk_fields ( key1 int NOT NULL,
                                         key2 int NOT NULL,
                                            constraint pk_test PRIMARY KEY (key1, key2),
                                         value1 int,
                                         value2 int)

DECLARE @Data_Table_Name sysname,
        @Data_Table_Key  sysname

SELECT @Data_Table_Name = 't_test_multiple_pk_fields'

SELECT Data_Table_Key = COLUMN_NAME 
  FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
 WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1 
  AND TABLE_NAME = @Data_Table_Name 

SELECT @Data_Table_Key = COLUMN_NAME 
  FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
 WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1 
  AND TABLE_NAME = @Data_Table_Name 

SELECT @Data_Table_Key

由于SQL Server是为批处理而设计的,因此应尽可能避免处理数据RBAR ,但我正在假设您在此问题上没有选择权。 考虑到这一假设,这应该实现您所追求的目标:

-- Generate some dummy data

Declare @Numbers Table
(
    Number_ID int primary key,
    Number varchar(100)
)

;with cte as
(
    select 1 [Value]
    union all
    select value+1 from cte where (value+1) < 9
)
insert into @Numbers(Number_ID, number)
select value, convert(varchar(100), value-1) from cte

-- Process the records, one at a time

declare @id int=0
declare @result table (value varchar(100))
while (1=1)
begin
    -- Get the ID of the next record for processing
    select @id=min(number_id) from @numbers where number_id>@id
    if (@id is null)
        break

    -- Call the useless stored procedure and dump the results into a temp table (table layout must match that of the resultset from the SP)
    insert into @result(value)
    exec UselessStoredProc @id

    -- Copy the value from the temp table to the appropriate record in your numbers table
    update n set Number=r.value
    from @numbers n
    cross join @result r
    where n.Number_ID=@id

    -- Clear out the temp table, ready for your next value
    delete from @result
end

-- Show the results
select * from @Numbers

我在这里使用WHILE循环遍历你的数字表,因为它比CURSOR更有效(或者我应该说效率更低)。

再说一次,如果有任何方法可以避免RBAR模式,那么我会去寻找它。

你的问题毫无意义,只是为了显示一个选项,这是一种使用表类型参数的方法。

创建一个名为udtNumbers的表类型。 这将用于将数字作为输入参数传递给过程。 最重要的是避免RBAR

create type udtNumbers(Number_ID int, Number int)

更改过程以接受表类型作为输入参数。

Alter PROCEDURE dbo.UselessStoredProc @inputNum udtNumbers READONLY
AS
SELECT Number_ID, 'lol my number is: ' + CONVERT(varchar(max), Number)
from @inputNum
GO

现在打电话给程序

Declare @inputNum udtNumbers

insert into @inputNum(Number_ID, Number)
select Number_ID, Number
from dbo.Numbers

exec dbo.UselessStoredProc @inputNum 

希望这可以帮助您以某种方式解决您的现实世界问题

绝对可以在视图中使用函数。 你没有说明应该如何修改数字,除了显示它减少1.这就是说该函数应该能够完成你想要的任何逻辑。 简单地说,这将是(使用AdventureWorks表)...

DROP FUNCTION IF EXISTS dbo.UselessFunction1 
go
CREATE FUNCTION dbo.UselessFunction1(@inputnum int)
RETURNS varchar(30)
AS
BEGIN
    DECLARE @outputString varchar(30)
    SELECT @outputString = 'lol my number is  ' + CAST(@inputnum-1 AS varchar(10))
    RETURN @outputString
END
GO
DROP VIEW IF EXISTS dbo.UselessView1
go
CREATE VIEW dbo.UselessView1 AS
SELECT Name,dbo.UselessFunction1(pc.ProductCategoryID) AS Number_ID
FROM Production.ProductCategory AS pc
GO
SELECT * FROM dbo.UselessView1 AS uv;

从性能角度来看,标量函数当然不是理想的,所以如果你可以通过单个查询来执行UselessStoredProc正在执行的任何逻辑,那么内联表值函数会更好,例如......

DROP FUNCTION IF EXISTS dbo.UselessFunction2
go 
CREATE FUNCTION dbo.UselessFunction2(@inputnum int)
RETURNS TABLE
AS
RETURN (
        SELECT 'lol my number is  ' + CAST(@inputnum-1 AS varchar(10)) AS Number_ID
        )
GO
DROP VIEW IF EXISTS dbo.UselessView2
go
CREATE VIEW dbo.UselessView2 AS
SELECT Name,x.Number_ID
FROM Production.ProductCategory AS pc
CROSS APPLY (SELECT Number_ID FROM dbo.UselessFunction2(pc.ProductCategoryID)) x
GO
SELECT * FROM dbo.UselessView1 AS uv;

另一种方法是预先制作一个“巨大”的视图,它结合了应用程序可能希望看到的所有可能的表。

例如,某些东西

CREATE TABLE t_tableA (keyA int NOT NULL PRIMARY KEY, valueA1 varchar(10), valueA2 datetime)
CREATE TABLE t_tableB (keyB int NOT NULL PRIMARY KEY, valueB1 int, valueB2 varchar(500), valueB3 int)

GO
CREATE VIEW v_all_tables
AS
SELECT table_name = 't_tableA', table_key = keyA, table_value = Convert(nvarchar(max), valueA1) + ' - ' + Convert(nvarchar(max), valueA2)
  FROM t_tableA

UNION ALL 

SELECT table_name = 't_tableB', table_key = keyB, table_value = '[' + Convert(nvarchar(max), valueB1) + '] ' + Convert(nvarchar(max), valueB2) + ' = ' + Convert(nvarchar(max), valueB3)
  FROM t_tableB

GO

SELECT * FROM v_all_tables WHERE table_name = 't_tableA'

这是一个视图,它将是“实时”,您可以轻松过滤掉您需要的表格。 这可能适用于数百个表。 但是,每当您向数据库添加新表时,您都需要更新视图以包含它! 您可能希望编写一个存储过程来删除/创建视图并在每晚或每当添加新表时运行它,而不是编写一个获取数据的存储过程。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM