2014-09-12 51 views
1

SQL Fiddle迭代查詢更改爲一個關係基於集合的查詢

我嘗試沒有成功改變迭代/光標查詢(也就是工作的罰款)爲關係集查詢,以實現更好的性能。

我有什麼:

表1

| ID | NAME | 
|----|------| 
| 1 | A | 
| 2 | B | 
| 3 | C | 


使用功能,我想我的數據插入到另一個表。下面的函數是一個簡化的例子:

功能

CREATE FUNCTION fn_myExampleFunction 
(
    @input nvarchar(50) 
) 

RETURNS @ret_table TABLE 
(
    output nvarchar(50) 
) 

AS 

BEGIN 
    IF @input = 'A' 
    INSERT INTO @ret_table VALUES ('Alice') 
    ELSE IF @input = 'B' 
    INSERT INTO @ret_table VALUES ('Bob') 
    ELSE 
    INSERT INTO @ret_table VALUES ('Foo'), ('Bar') 
RETURN 
END; 


我的預期的結果是在表2中插入數據如下所示:

表2

| ID | NAME | 
|----|-------| 
| 1 | Alice | 
| 2 | Bob | 
| 3 | Foo | 
| 3 | Bar | 


爲了達到這個目的,我嘗試了一些CTE(通用表表達式)和關係查詢,但都沒有按照需要工作。到目前爲止,我唯一的工作解決方案是一種迭代式而非表現性解決方案。

我目前工作的解決方案

BEGIN 
    DECLARE 
    @ID int, 
    @i int = 0, 
    @max int = (SELECT COUNT(name) FROM table1) 

    WHILE (@i < @max) -- In this example, it will iterate 3 times 
    BEGIN 

    SET @i += 1 

    -- Select table1.ID where row_number() = @i 
    SET @ID = 
      (SELECT 
      id 
      FROM 
      (SELECT 
       id, 
       ROW_NUMBER() OVER (ORDER BY id) as rn 
      FROM 
       table1) rows 
      WHERE 
       rows.rn = @i 
     ) 

    -- Insert into table2 one or more rows related with table1.ID 
    INSERT INTO table2 
     (id, name) 
    SELECT 
     @ID, 
     fn_result.output 
    FROM 
     fn_myExampleFunction (
     (SELECT name FROM table1 WHERE id = @ID) 
    ) fn_result 

    END 
END 


的目標是達到同樣不通過的ID迭代。

回答

2

如果問題是如何在一組面向的方式將一個函數,然後cross apply(或outer apply)是你的朋友:

insert into table2 (
    id, name 
) select 
    t1.id, 
    t2.output 
from 
    table1 t1 
     cross apply 
    fn_myExampleFunction(t1.name) t2 

Example SQLFiddle

如果函數的非精簡版是適合於重寫,其他解決方案可能會更快。

+1

這是正確的答案 – Kik 2014-09-12 22:32:55

+0

@Laurence,謝謝!我真正的功能很複雜(分割字符串),你的建議會更適合我。 – Zanon 2014-09-12 23:26:47

1

這樣的查詢會做你想要什麼:

insert into table2(id, name) 
    select id, (case when name = 'A' then 'Alice' 
        when name = 'B' then 'Bob' 
        when name = 'C' then 'Foo' 
       end) 
    from table1 
    union all 
    select id, 'Bar' 
    from table1 
    where name = 'C'; 
1

爲什麼不將這些數據存儲爲表?這是關係。在一個函數或存儲過程中對它進行編碼似乎並不理想。

在任何情況下,我希望以下內容爲您提供有關如何改進代碼的想法。我意識到你說你的函數比你的例子更復雜,但你仍然可以在必要時使用這個想法。

INSERT dbo.table2 (ID, Name) 
SELECT 
    T1.ID, 
    N.FullName 
FROM 
    dbo.table1 T1 
    INNER JOIN (VALUES -- A "derived table" made up of only constants 
     ('A', 'Alice'), 
     ('B', 'Bob'), 
     ('C', 'Foo'), 
     ('C', 'Bar') 
) N (ShortName, FullName) 
     ON T1.Name = N.ShortName 
; 

當然不過,這可能只是呈現INNER JOIN dbo.NameTranslation N如果它是在一個真正的表(然後更新它會容易得多!)。

如果你的功能絕對不能被改寫爲關係(它必須在一個時間一個單一的名稱),那麼你可以使用CROSS APPLY

INSERT dbo.table2 (ID, Name) 
SELECT 
    T1.ID, 
    N.OutputName 
FROM 
    dbo.table1 T1 
    CROSS APPLY dbo.YourFunction(T1.Name) F 
; 

然而,這並不會表現非常好,爲大行集。將該功能重寫爲RETURNS TABLE是朝着正確方向邁出的一步(而不是RETURNS @variable TABLE (definition))。

+0

謝謝@ErikE。我真正的功能是複雜的(分割字符串),不能插入到表中,但是根據我寫的問題,這是一個很好的建議。投票給你。 – Zanon 2014-09-12 23:40:50

+1

如果您正在尋找速度,CLR可能是分割字符串的最佳選擇。請參閱[在SQL性能上關於此主題的Aaron Bertrand](http://sqlperformance.com/2012/07/t-sql-queries/split-strings)。 – ErikE 2014-09-13 00:35:28