2017-11-06 9 views
0

を選択する機能をストアド:変換は、私は価格の値を計算する機能を持っているウィンドウ関数

ALTER FUNCTION [dbo].[LAWI_DEinkauf](@sArtikelID varchar(36)) 
RETURNS NUMERIC(14, 2) 
AS 
BEGIN 
    DECLARE @menge DECIMAL(16,6) 
    DECLARE @tmenge DECIMAL(16,6) 
    DECLARE @wert DECIMAL(16,6) 
    DECLARE @ekpreis DECIMAL(16,6) 
    DECLARE @ekmenge DECIMAL(16,6) 

    DECLARE @myposI CURSOR 

    SET @ekpreis = 0 
    SET @ekmenge = 0 
    SET @wert = 0 
    SET @menge = 0 
    SET @tmenge = 0 

    SET @myposI = CURSOR SCROLL FOR 
     SELECT einkaufspreis, menge 
     FROM lawi_bewegung 
     WHERE artikelid = @sArtikelID 
     ORDER BY datum, ident; 

    OPEN @myposI 

    FETCH NEXT FROM @myposI INTO @ekpreis, @ekmenge 

    WHILE @@fetch_status = 0 
    BEGIN 
     SET @tmenge = @tmenge + @ekmenge 

     IF @ekpreis <> 0 
      SET @menge = @menge + @ekmenge 

     SET @wert = @wert + @ekpreis * @ekmenge 

     IF @tmenge = 0 
      SET @wert = 0 

     IF @tmenge = 0 
      SET @menge = 0 

     FETCH NEXT FROM @myposI INTO @ekpreis, @ekmenge; 
    END 

    CLOSE @myposI 
    DEALLOCATE @myposI 

    IF @menge = 0 
     SET @menge = 1 

    RETURN @wert/@menge 
END 

私は窓関数で選択するには、この保存された機能を変換しよう。 Reasonは、多くの行を持つselectでこれを使用すると、ストアド関数のパフォーマンスが低下します。

一般的に、これは以下のように働いているが、以下:

あり
(SELECT TOP 1 
    wert/CASE WHEN menge = 0 THEN 1 ELSE menge END 
FROM 
    (SELECT 
      SUM(menge) OVER (ORDER BY datum, ident) as tmenge, 
      SUM(CASE WHEN einkaufspreis <> 0 THEN menge ELSE 0 END) OVER (ORDER BY datum, ident) AS menge, 
      SUM(einkaufspreis * menge) OVER (ORDER BY datum, ident) AS wert, 
      ROW_NUMBER() OVER (ORDER BY datum, ident) AS rn 
     FROM 
      lawi_bewegung 
     WHERE 
      artikelid = XXXX) a order by rn desc 

、XXXXは、パラメータsArtikelID(外から私の場合は選択)になります。

IF @tmenge = 0 
    SET @wert = 0 
IF @tmenge = 0 
    SET @menge = 0 

にはどうすればウィンドウ関数を使用している私の選択にこのロジックを含めることができます。

私の問題は、和がリセットされ保存された機能の一部ですか?

+0

本当にカーソルが必要ですか? –

+0

この関数を破棄する必要があります。犯罪はありませんが、これはあなたのシステムからパフォーマンスを吸うことになります。それはパフォーマンスにとって恐ろしいスカラー関数です。しかし、それはパフォーマンスのためにさらに悪い内部のカーソルで悪化します。これは、インラインテーブル値関数として書き直す必要があります。しかし、私たちはもっと多くの情報を必要としています。ここから始めましょう。 http://spaghettidba.com/2015/04/24/how-to-post-a-t-sql-question-on-a-public-forum/ –

+0

これは恐ろしいので、私はそれをwindowing-functionに変更したい。これは合計をリセットする部分を除いて動作しています。 – BennoDual

答えて

2

次のクエリは問題を解決しているようですが、それがうまくいくかどうかはわかりません。いくつかのデータで試してみて、値と性能をチェックしてください。

WITH step1 AS (
    SELECT 
     *, 
     CASE WHEN SUM(menge) OVER (PARTITION BY artikelid ORDER BY datum, ident) = 0 THEN 1 ELSE 0 END AS reset 
    FROM lawi_bewegung 
), 
step2 AS (
    SELECT *, 
     SUM(reset) OVER (PARTITION BY artikelid ORDER BY datum, ident) - reset AS g 
    FROM step1 
), 
step3 AS (
    SELECT artikelid, 
     SUM(CASE WHEN einkaufspreis <> 0 THEN menge ELSE 0 END) 
      OVER (PARTITION BY artikelid, g ORDER BY datum, ident) AS menge, 
     SUM(einkaufspreis * menge) 
      OVER (PARTITION BY artikelid, g ORDER BY datum, ident) AS wert, 
     ROW_NUMBER() OVER (PARTITION BY artikelid ORDER BY datum, ident) AS rn 
    FROM step2 
) 

SELECT i.artikelid, 
    d.* 
FROM lawi_inventur i 
CROSS APPLY (
    SELECT TOP 1 
     CAST(wert/CASE WHEN menge = 0 THEN 1 ELSE menge END AS DECIMAL(14, 2)) AS DEinkauf 
    FROM step3 s 
    WHERE s.artikelid = i.artikelid 
    ORDER BY rn DESC 
) d 
; 

それが動作する方法は以下の通りです:

  • ステップ1はあなたのリセットポイントである、tmengeが0である行をマークします。
  • ステップ2は、reset列で実行中の合計を行い、効果的にグループ化列を作成します。 tmengeの合計が0になるたびに、次の行は異なるグループ化値を持ちます。
  • ステップ3では、このグループ化値を利用して、このグループ化値を使用して、OVER句のPARTITION BYオプションを使用して行を分割します。また、すべての行についてROW_NUMBER()を計算し、最後の行を選択するために外部クエリで使用されます。

中間ステップの出力を見ると分かりやすいかもしれません。最後のSELECT(最後の4行)をSELECT * from step1またはSELECT * FROM step2などに変更してください。

このクエリのインデックスが良い場合は、ON (artikelid, datum, ident) INCLUDE (menge, einkaufspreis)のようになります。

+0

これは素晴らしい動作します。もう1つの質問:これを他の選択肢にどのように埋め込むことができますか?私は列ArtikelIDを持つテーブルLAWI_Inventurを持っています。今私はLAWI_Inventurから選択を行い、lawi_inventur(行のArtikelIDを持つ)の各行の価格を計算します。 – BennoDual

+0

必要な変更を加えて回答を編集しましたが、これは少し遅いかもしれません。試してもらえますか? – kirchner

関連する問題