2012-05-22 7 views
6

En mi DB Tengo dos tablas Artículos (id, ..., ToatlViews int) y ItemViews (id, ItemId, fecha y hora)actualización número de columnas a partir de datos de otra tabla

En la tabla I ItemViews almacenar todos los puntos de vista de un artículo cuando llegan al sitio. De vez en cuando deseo llamar a un procedimiento almacenado para actualizar el campo Items.ToatlViews. Intenté hacer este SP usando un cursor ... pero la declaración de actualización es incorrecta. ¿Me puedes ayudar a corregirlo? ¿Puedo hacer esto sin cursor?

CREATE PROCEDURE UpdateItemsViews 
AS 
BEGIN 
    -- SET NOCOUNT ON added to prevent extra result sets from 
    -- interfering with SELECT statements. 
    SET NOCOUNT ON; 

    DECLARE @currentItemId int 
    DECLARE @currentItemCursor CURSOR 
    SET @currentItemCursor = CURSOR FOR SELECT Id FROM dbo.Items 

    OPEN @currentItemCursor 
    FETCH NEXT FROM @currentItemCursor INTO @currentItemId 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
     Update dbo.Items set TotalViews = count(*) 
       from dbo.ItemViews where [email protected] 
     FETCH NEXT FROM @currentItemCursor INTO @currentItemId 
    END 
END 
GO 
+0

Te recomiendo que hagas todos los esfuerzos posibles para no utilizar los cursores cuando escribas SQL, ya que siempre habrá una forma basada en 'set' para escribir lo que quieras contra la base de datos. Por supuesto, siempre habrá una excepción a esta regla. –

Respuesta

18

Se puede utilizar una instrucción UPDATE directa

update Items set TotalViews = 
    (select COUNT(id) from ItemViews where ItemViews.ItemId = Items.Id) 

Es posible que desee probar el rendimiento de las distintas maneras de hacer esto si eso es importante

+0

+1 por elegancia. Hablando de rendimiento; Recuerdo haber leído en alguna parte que 'count (1)' en lugar de 'count (id)' es mejor (aunque probablemente no lo notarías). Dado que el campo de identificación no necesita ser seleccionado como parte de la consulta ... –

+1

@mouters es una idea falsa. 'COUNT (1)' solo es mejor que 'COUNT (id)' si estamos hablando de precisión, y solo es más preciso (o diferente en cualquier forma) si 'id' es anulable. Si ve esto en algún lugar (que no sea su memoria), indíquelo porque debe corregirse o aclararse. –

+0

Comentario justo: creo que tendré dificultades para encontrar el artículo nuevamente. –

8

Usted podría utilizar update ... from lugar de un cursor:

update i 
set  TotalViews = iv.cnt 
from dbo.Item i 
join (
     select ItemId 
     ,  count(*) as cnt 
     from dbo.ItemViews 
     group by 
       ItemId 
     ) iv 
on  i.Id = iv.ItemId 
2
;WITH x AS 
(
    SELECT ItemID, c = COUNT(*) 
    FROM dbo.ItemViews 
    GROUP BY ItemID 
) 
UPDATE i 
SET TotalViews = x.c 
FROM dbo.Items AS i 
INNER JOIN x 
ON x.ItemID = i.ItemID; 

¿Pero por qué quiere guardar este valor, cuando siempre puede obtener el conteo en tiempo de ejecución? Deberá ejecutar esta declaración de actualización cada vez que toque la tabla ItemViews de alguna manera, de lo contrario, el recuento almacenado con Elementos será incorrecto.

Lo que se puede considerar hacer lugar es la creación de una vista indizada:

CREATE VIEW dbo.ItemViewCount 
WITH SCHEMABINDING 
AS 
    SELECT ItemID, ItemCount = COUNT_BIG(*) 
     FROM dbo.ItemViews 
     GROUP BY ItemID; 
GO 
CREATE UNIQUE CLUSTERED INDEX x ON dbo.ItemViewCount(ItemID); 

Ahora usted puede unirse a la vista en sus consultas y saber que el recuento es siempre al día (sin tener que pagar la pena de escanear para el conteo de cada artículo). La desventaja de la vista indizada es que paga ese costo de forma incremental cuando hay inserciones/actualizaciones/eliminaciones en la tabla ItemViews.

0

Encontré esta pregunta/respuesta un año después de haber sido escrita y respondida. la respuesta fue correcta, pero estaba buscando algo un poco más automático. Terminé escribiendo un activador para recalcular automáticamente la columna cuando se insertó, borró o actualizó una fila relevante en la otra tabla.

creo que es una solución mejor que correr algo de forma manual para hacer el cálculo ya que no hay ninguna posibilidad de que alguien olvidar para ejecutar el código:

CREATE TRIGGER [dbo].[TriggerItemTotalViews] 
    ON [dbo].[ItemViews] 
    AFTER INSERT, DELETE, UPDATE 
AS 
BEGIN 
SET NOCOUNT ON; 

UPDATE [Items] 
SET [TotalViews] = 
    (
    SELECT COUNT(id) 
    FROM [ItemViews] 
    WHERE [ItemViews].[ItemId] = [Items].[ItemId] 
    ) 
WHERE [Items].[ItemId] IN 
    (
    SELECT [ItemId] FROM [INSERTED] 
    UNION 
    SELECT [ItemId] FROM [DELETED] 
    ) 
END 
0

Lo mismo pero diferente:

declare @productId int = 24; 
declare @classificationTypeId int = 86; 

update s 
set CounterByProductAndClassificationType = row_num 
from Samples s 
join 
(
    select row_number() over (order by (select Id)) row_num, Id 
    from Samples 
    where 
     ProductId = @productId and 
     ClassificationTypeId = @classificationTypeId 
) s_row on s.Id = s_row.Id 
Cuestiones relacionadas