【问题标题】:Fire a trigger on every insert to update a table with calculate tf-idf在每次插入时触发触发器以使用计算 tf-idf 更新表
【发布时间】:2019-12-16 01:17:21
【问题描述】:

我有一个 SQL Server 数据库,其中包含来自 ASP.NET C# 程序的术语作为此存储过程的单词列表:

CREATE PROCEDURE [dbo].[InsertTerms]
    (@dt AS dbo.EngTerms READONLY)
AS
BEGIN
    DECLARE @AllDocs FLOAT, @DocsWithText FLOAT,  
            @TF FLOAT, @IDF FLOAT, @Weight FLOAT, @Index_ID INT;

    SET NOCOUNT ON;

    INSERT INTO dbo.EnIndex (Term, TF, IDF, Weight, Doc_ID) 
        SELECT Term, TF, IDF, Weight, Doc_ID 
        FROM @dt;

插入这些术语后,我需要通过触发器为它们中的每一个计算IDFWeight,该触发器使用公式更新每个插入的行并更新行。

直到现在我才得到这个只更新最后一行:

ALTER TRIGGER [dbo].[UpdateIndex] 
ON [dbo].[EnIndex]
AFTER INSERT
AS 
BEGIN
    SET NOCOUNT ON;
    DECLARE @AllDocs FLOAT, @DocsWithText FLOAT,  
            @TF FLOAT, @IDF FLOAT, @Weight FLOAT, @Index_ID INT;

    SET @Index_ID = (SELECT IDENT_CURRENT ('EnIndex'));
    SET @AllDocs = (SELECT COUNT(Doc_ID) AS AllDocs FROM Document);
    SET @DocsWithText = (SELECT COUNT(Distinct(Doc_ID)) FROM EnIndex WHERE Index_ID = @Index_ID); 
    SET @TF = (SELECT DISTINCT TF FROM EnIndex WHERE Index_ID = @Index_ID);
    SET @IDF = (LOG(@AllDocs / @DocsWithText));
    SET @Weight = (@TF * @IDF);

    UPDATE EnIndex 
    SET IDF = @IDF, Weight = @Weight 
    WHERE Index_ID = @Index_ID;
END

任何建议,我都阅读了有关 stackoverflow 的所有问题,它们对完成这一步非常有帮助。

【问题讨论】:

  • 您必须使用 inserted 伪表,然后使用游标(或临时表循环)一次处理每一行 1。它不是推荐的做法,因为它在 SQL Server 中表现不佳 - 它在客户端中做得更好。但如果你不得不这样做。例如stackoverflow.com/questions/5805413/…
  • 你似乎还有一个额外的Index_ID = @Index_ID 似乎没有做任何事情?
  • 我修复了它,我之前看到了链接,但它不起作用
  • “没用”是什么意思?您必须以某种方式遍历行,这是一个好的开始,然后根据您的需要进行调整。从简单开始,构建一个循环,它只打印行中的详细信息并从那里开始工作。
  • 为什么需要触发器?如果该过程可以手动运行,则创建一个返回结果的存储过程

标签: sql-server stored-procedures triggers


【解决方案1】:

评论太长了,所以我写这篇文章是为了告诉你什么似乎是一个重大错误。也许您当前的触发器更新了一行 - 但它看起来不像计算一个准确的值。

首先,你犯了一个常见的错误。当插入的表包含任意数量的行 - 从零(是!)到任意数量(不仅仅是一个)时,您的触发器必须正常工作。

接下来,您的表有一个标识列。您可以访问为插入表中的每一行生成的值,以“知道”插入了哪些行。在大多数情况下,使用 ident_current 通常不是一件有用的事情。

接下来,你有这个代码:

SET @DocsWithText = (SELECT COUNT(Distinct(Doc_ID)) FROM EnIndex WHERE Index_ID = @Index_ID);

我们知道 Index_ID 变量包含(假定为单个)插入行的标识值。因此,您正在计算单行中的值。这有任何意义吗?更糟糕的是,您正在计算不同的值。那更是无稽之谈。可以由此计算的唯一值是 1。稍后您将另一个数字除以该值 - 使该数字保持不变。不知道您的意图,但这没有任何用处。

你有这个:

SET @TF = (SELECT DISTINCT TF FROM EnIndex WHERE Index_ID = @Index_ID);

再次,我们看到了与上面讨论的相同的缺陷。您的查询选择单行,因为它按标识值过滤。使用 distinct 是没有意义的,因为查询只会选择一行。我感觉到您在没有真正考虑的情况下将 DISTINCT 添加到代码中。一般来说,根据我的经验,很少需要 DISTINCT。

所以我认为是时候退后一步,将您的逻辑开发为一个简单的 tsql 脚本。使用您从希望用于测试的某个随机列中分配标识值的变量来编写它。检索并计算正确计算 IDF 和权重所需的值。一旦您看到它确实如此,您就可以尝试将其转换为编写良好的触发器,该触发器适用于任意数量的插入行。

declare @id int, ...; 
declare @AllDocs int, @DocsWithText int; -- values that are counts from table  
declare @TF FLOAT, @IDF FLOAT, @Weight FLOAT; -- computed values

set @id = 34; -- set to an existing row for development 

set @AllDocs = (SELECT COUNT(Doc_ID) AS AllDocs FROM Document);
set @DocsWithText = ( ... ); 
set @TF = ( ... );
set @IDF = LOG(@AllDocs / @DocsWithText);
set @Weight = (@TF * @IDF);

select *, @AllDocs as AllDocs, @DocsWithText as DocsText, @TF as TF, 
    @IDF as IDF, @Weight as Wgt
from dbo.EnIndEx where Index_ID = @id; 

这只是一个开始 - 您需要调整它并处理逻辑。一旦计算出正确的值,您就可以开始将其转换为触发器。坦率地说,您需要掌握基本的 tsql 知识以及如何开发/调试代码。正如已经指出的,像“不起作用”这样的短语对任何人都没有用。所以我把how to ask smart questions 的讨论留给你 - 相当古老但仍然非常好的信息。

【讨论】:

  • 当我试图传递术语时,我在 @DocsWithText 上使用了 count 和 distinct(可能会重复)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-08
  • 2022-01-15
  • 1970-01-01
  • 2016-10-13
  • 2011-12-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多