2017-08-08 73 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