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

时间:2017-01-11 14:21:57

标签: sql sql-server sql-server-2012

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

我有一张桌子:

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]

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

P.S。请不要告诉我"只是这样做:

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

"

我很清楚我能做到这一点 - 这显然是一个例子,真正的代码要复杂得多,需要 STORED PROCEDURE 。注意,我故意从样本存储过程中返回一个字符串 - 我需要复杂的值,而不仅仅是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分钟写一个表,第三方软件使用该表。我感谢所有的帮助。我接受了最能帮助我的答案,但是循环答案也很有帮助,并得到了我需要的东西,尽管表现令人无法接受。

5 个答案:

答案 0 :(得分:4)

基于

  

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

Profit.flag <- as.integer(Profit[,1] >= 0)

我很困惑。为什么要在动态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

并在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 变量中得到相同的结果。

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

一些示例代码:

@Data_Table_Key

答案 1 :(得分:2)

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

-- 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模式,那么我就去做吧。

答案 2 :(得分:1)

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

创建一个名为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 

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

答案 3 :(得分:1)

绝对可以在视图中使用函数。你没有说明应该如何修改数字,除了显示它减少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;

答案 4 :(得分:1)

另一种方法是制作一个巨大的&#39;查看前端,它结合了应用程序可能希望查看的所有可能的表。

e.g。

的内容
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'

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

相关问题