簡體   English   中英

將迭代查詢更改為基於關系集的查詢

[英]Change an iterative query to a relational set-based query

SQL小提琴

我試圖將迭代/游標查詢(工作正常)更改為關系集查詢以實現更好的性能沒有成功。

我有的:

表格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;


我的預期結果是將數據插入到table2中,如下所示:

表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的情況下實現相同目標。

如果問題是關於如何以面向集合的方式應用功能,那么cross apply (或outer apply )是您的朋友:

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

示例SQLFiddle

如果您的函數的非簡化版本可以重寫,則其他解決方案可能會更快。

這樣的查詢將滿足您的要求:

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';

您為什么不將這些數據存儲為表格? 這是關系的。 將其編碼在函數或存儲過程中似乎不太理想。

無論如何,我希望以下內容為您提供有關如何改進代碼的想法。 我意識到您說您的函數比示例更復雜,但是即使在必要時,您仍然可以在函數內部使用此想法。

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) )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM