2017-08-08 123 views
0

的結果如果任何人都可以,甚至只是幫助我詞組這個問題更好,我將不勝感激。SQL服務器 - 更新所有的記錄,每組,子查詢

我有一個SQL Server表,讓我們把它的汽車,其中包含約佔它們的主人,包括car_id,owner_accountNumber,owner_numCars項目和信息的條目。

我們正在使用的排序基於擁有的汽車數「所有者importantness」,並依賴於owner_numCars列這樣做的系統。如果合理可行,我寧願不調整這一點。

有沒有一種方法,我可以使用存儲過程更新每owner_accountNumber owner_numCars?也許一些其他更有效的方式,我可以完成每個owner_numCars包含每個owner_accountNumber的條目數?

現在我能想到做到這一點的唯一方法是(從C#應用程序):

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

,並通過查詢

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

返回然後的foreach排但這似乎瘋狂與使服務器處理邏輯和更新相比效率低下。

編輯 - 感謝所有幫助。我知道這不是一個很好的建立數據庫,但這是我必須與之合作。我感謝大家的意見和建議。

回答

1

該解決方案考慮到您要保留CAR表中的owner_numCars列,並且該列應始終準確實時。

我將表格CARS定義爲包含當前所有者的汽車屬性表。當前所有者擁有的汽車數量被解除標準化到此表中。說我,LAS,擁有三輛車,那麼有表CARS三個條目,因爲這樣的:

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

對於owner_numCars在現場接口被用作重要的因素,你需要更新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

您有一個有效的觀點,如果您想要維護數百萬條記錄並且緩存了非標準化值,那麼在插入/更新/刪除期間最好保持該值。並且有一個地方可以在存儲過程中進行。但是,在一次處理單個owner_accountNumber時,派生列與窗口函數的差異仍然不會很大。展開您的答案並展示如何編寫存儲過程。或者我通常不喜歡說這是一個觸發器。因爲SP除非使用自定義類型,否則不允許多行更改 – Matt

+0

是的,我明白了你的觀點。我不會這樣設計它。我想到觸發器,但不想在這裏討論這個話題。 – LAS

+0

同意我做幾乎所有可能的東西,遠離觸發器,尤其是在處理數百萬行時:) – Matt

1

有很多的這樣做的方法。以下是使用COUNT() OVER窗口函數和可更新Common Table Expression [CTE]的一種方法。那你會不會擔心與數據回來,IDS等

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

UPDATE cteCarCounts 
SET owner_numCars = NewNumberOfCars 

然而,從設計的角度來看,我會提高,這是否值(owner_numCars)的問題應該是在這個表上或在什麼我假設將是所有者表。

如果您希望數據始終反映當前值,Rominus確實使用了一個視圖。你也可以使用表值函數,它可以比視圖更高效。但是,如果你只是顯示它,那麼你可以簡單地做這樣的事情:

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

通過添加where子句無論是CTE或SELECT語句,你將有效地限制你的數據集和解決方案應保持快。例如。

WHERE owner_accountNumber = @owner_accountNumber 
+0

此解決方案更新每一行。如果有數百萬條記錄並且列需要實時維護,則不會很好地擴展。 – LAS

1

我想你需要的是一個視圖。如果你不知道,View是一個虛擬表,它顯示/計算來自真實表的數據,該數據表隨着表數據更新而不斷更新。所以,如果你想看到你的表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列,因爲你並不需要實際數據存儲在每一行。如果您無法刪除它,則可以使用除owner_numCars之外的所有字段的明確列表替換a.*

1

你不想運行SQL更新此值。如果它不運行很長時間會怎麼樣?如果某人加載大量數據然後運行得分並發現一輛擁有100輛汽車的人算作零B/C,那麼更新沒有運行。數據應該只生活在1個地方,更新生活在2個地方。你需要一個視圖,根據需要從表中提取這個值。

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數據模型可能需要打破第三範式。我不認爲這是這種情況之一。所有事情的答案都以「它取決於...」開頭。 – JBrooks