2017-08-08 14 views
0

誰かが私のフレーズを援助することができたとしても、SQL Server - サブクエリの結果でグループごとにすべてのレコードを更新

私はSQL Serverテーブルを持っています。車と呼ぶことにしましょう。car_id、owner_accountNumber、owner_numCarsなどの所有者に関する項目と情報を表すエントリが含まれています。

私たちは所有している車の台数に基づいて「所有者の重要性」を分類するシステムを使用しています。そのためにowner_numCars列に依存しています。合理的に可能であれば、これを調整しないでください。

ストアドプロシージャを使用してowner_accountNumberごとにowner_numCarsを更新する方法はありますか?多分私はowner_accountNumberごとのエントリの数を含むすべてのowner_numCarsを達成することができる他のより効率的な方法?

今、私はこれを行うに考えることができる唯一の方法は、へ(C#アプリケーションから)です:

SELECT owner_accountNumber, COUNT(*) 
FROM mytable 
GROUP BY owner_accountNumber; 

、その後、foreachの行は

UPDATE mytable 
SET owner_numCars = <count result> 
WHERE owner_accountNumber = <accountNumber result> 

、そのクエリによって返された。しかし、これは乱暴に思えますサーバーがロジックと更新を処理するのに比べて非効率的です。

編集 - すべての助けをいただきありがとうございます。私はこれが本当にうまくセットアップされたデータベースではないことを知っていますが、それは私が作業しなければならないものです。私は皆様のご意見とアドバイスを感謝します。

答えて

1

このソリューションでは、CARsテーブルのowner_numCars列を保持し、列が常に正確にリアルタイムである必要があることを考慮しています。

私はテーブルCARSを、現在の所有者を含む自動車に関する属性を持つテーブルとして定義しています。現在の所有者が所有する車の台数は、このテーブルに正規化されていません。私は、LASは、3台の車を所有していると言う、そしてテーブルCARSに3つのエントリのような、ありますowner_numCarsについては

 
    car_id owner_accountNumber owner_numCars 
     1   LAS1      3 
     2   LAS1      3 
     3   LAS1      3 

はライブインタフェースで重要な因子として使用されるように、あなたがのためにowner_numCarsを更新する必要があるだろうLAS1は車を売ったり買ったり、列車に乗ったり車列に乗り込んだりするたびに、すべての車に乗っています。

古いオーナーと新しいオーナーの両方にCARSを更新する必要があります。 Samがcar1を購入した場合、SamとLASの合計を更新する必要があります。

この手順を使用して行を更新できます。このSPは非常に文脈に敏感です。削除または挿入された所有者に対して行が削除または挿入された後で呼び出す必要があります。所有者が更新されると、古い所有者と新しい所有者の両方に呼び出される必要があります。

create procedure update_car_count 
@p_acct nvarchar(50) -- use your actual datatype here 

AS 

    update CARS 
    set owner_numCars = (select count(*) from CARS where owner_accountNumber = @p_acct) 
    where owner_accountNumber = @p_acct; 

GO 

をすべてaccount_ownersを更新するには:変更の所有者アカウントとしてリアルタイムに更新するには

create procedure update_car_count_all 
AS 

    update C 
    set owner_numCars = (select count(*) from CARS where owner_acctNumber = C.owner_acctNumber) 
    from CARS C 
GO 
+0

あなたは、何百万ものレコードを維持し、挿入/更新/削除中にその値を維持するのに最適であるキャッシュされた非正規化値を持つ場合に有効な点があります。そして、それを行う1つの場所はストアドプロシージャになります。ただし、一度に1つのowner_accountNumberを処理する場合、派生列はウィンドウ関数との大きな違いはありません。あなたの答えを広げ、ストアドプロシージャがどのように書けるかを示します。または、私は通常、これをトリガと言って嫌いです。カスタムタイプを使用しない限りSPは複数の行の変更を許可しないため、 – Matt

+0

はい、あなたのポイントが表示されます。私はこのようにデザインしません。私はトリガーを考えましたが、ここでその話題に取り掛かりたくありませんでした。 – LAS

+0

合意私はトリガーから離れて何百万という行を扱うときにはほとんどすべて可能です:) – Matt

1

これにはさまざまな方法があります。ここでは、ウィンドウ関数COUNT() OVERを使用し、更新可能なCommon Table Expression [CTE]を使用する1つの方法があります。あなたは、私がこの値かどうか(owner_numCars)の質問を引き上げる設計の観点から、

;WITH cteCarCounts AS (
    SELECT 
     owner_accountNumber 
     ,owner_numCars 
     ,NewNumberOfCars = COUNT(*) OVER (PARTITION BY owner_accountNumber) 
    FROM 
     MyTable 
) 

UPDATE cteCarCounts 
SET owner_numCars = NewNumberOfCars 

しかし、IDSなど、バックデータを関係を心配する必要がないことを、このテーブルの上または上がどうあるべきかI所有者テーブルと仮定します。

Rominusは、データに常に現在の値が反映されるようにするには、ビューを使用することをお勧めしました。また、ビューよりもパフォーマンスが高い可能性のあるテーブル値関数を使用することもできます。 CTEまたはSELECT文のあなたが効果的にあなたのデータセットを制限しますし、溶液が速いままにしてくださいいずれかにwhere句を追加することにより

SELECT 
    owner_accountNumber 
    ,owner_numCars = COUNT(*) OVER (PARTITION BY owner_accountNumber) 
FROM 
    MyTable 

:あなたは、単にそれを見せている場合しかし、あなたは、単にこのような何かを行うことができます。例えば。

WHERE owner_accountNumber = @owner_accountNumber 
+0

このソリューションはすべての行を更新します。何百万ものレコードがあり、その列をリアルタイムで管理する必要がある場合、それはうまく拡張されません。 – LAS

1

私はあなたが必要と思うのはビューです。わからない場合、ビューは、テーブルデータの更新時に連続的に更新される実際のテーブルのデータを表示/計算する仮想テーブルです。あなたがowner_numCarsであなたのテーブルを見たいのであれば、あなたが行うことができ、コメントを追加しました:

SELECT a.*, b.owner_numCars 
from mytable as a 
inner join 
    (SELECT owner_accountNumber, COUNT(*) as owner_numCars 
    FROM mytable 
    GROUP BY owner_accountNumber) as b 
on a.owner_accountNumber = b.owner_accountNumber 

あなたが実際にそれぞれの行にそのデータを保存する必要はありませんので、実際のテーブルからowner_numCars列を削除したいと思います。削除できない場合は、a.*を、owner_numCarsを除くすべてのフィールドの明示的なリストに置き換えることができます。

1

あなたは、この値を更新するためにSQLを実行する必要はありません。それは長い間実行されない場合はどうなりますか?誰かが大量のデータを読み込んでスコアを実行し、更新が実行されなかった100台の車が0台の男を見つけた場合はどうなりますか?データは1つの場所にのみ存在する必要があります。更新する必要がある場合は、テーブルからこの値を引き出すビューが必要です。

CREATE VIEW vOwnersInfo 
AS 
SELECT o.*, 
ISNULL(c.Cnt,0) AS Cnt 
FROM OWNERS o 
LEFT JOIN 
     (SELECT OwnerId, 
     COUNT(1) AS Cnt 
     FROM Cars 
     GROUP BY OwnerId) AS c 
ON o.OwnerId = c.OwnerId 
+0

私はこのデータを車のテーブルに住んでいることに同意するが、悪いです。 OLAPデータモデルのフラット化されたファクトテーブルと見なされます。潜在的にSSASクエリを高速化し、列インデックスストアを利用できるような値を複製する必要がある場合があります。 – Matt

+0

@Matt正しいです、OLAPデータモデルは3番目の正規形を破る必要があります。私はこれがそのようなケースの一つだとは思わない。すべての答えは「それは...」から始まります。 – JBrooks

関連する問題