代码之家  ›  专栏  ›  技术社区  ›  Daniel Revell

有效地查找数据库表中的唯一值

  •  4
  • Daniel Revell  · 技术社区  · 16 年前

    我有一个包含大量行的数据库表。此表表示系统记录的消息。每个消息都有一个消息类型,它存储在表中自己的字段中。我正在写一个网站来查询这个消息日志。如果我想按消息类型进行搜索,那么理想情况下,我希望有一个下拉框列出数据库中出现的消息类型。消息类型可能会随着时间的推移而改变,因此我无法将这些类型硬编码到下拉列表中。我得查一下。遍历整个表内容以找到唯一的消息值显然是非常愚蠢的,但是在数据库字段中是愚蠢的,我在这里要求一种更好的方法。也许一个单独的查找表,数据库偶尔更新它,列出我可以填充下拉列表的唯一消息类型是一个更好的主意。

    如有任何建议,将不胜感激。

    我使用的平台是asp.net mvc和sql server 2005

    9 回复  |  直到 16 年前
        1
  •  9
  •   Yuriy Faktorovich    16 年前

    一个单独的查找表,其中包含存储在日志中的消息类型的ID。这将减小日志的大小并提高日志的效率。也会 Normalize 你的数据。

        2
  •  5
  •   AdaTheDev    16 年前

    是的,我肯定会用单独的查找表。然后,您可以使用以下内容填充它:

    INSERT TypeLookup (Type)
    SELECT DISTINCT Type
    FROM BigMassiveTable
    

    然后可以定期运行一个充值作业,从主表中提取查找表中不存在的新类型。

        3
  •  2
  •   Quassnoi    16 年前
    SELECT  DISTINCT message_type
    FROM    message_log
    

    是最直接但效率不高的方法。

    如果您有一个类型列表,可以 可能地 出现在日志中,使用以下命令:

    SELECT  message_type
    FROM    message_types mt
    WHERE   message_type IN
            (
            SELECT  message_type
            FROM    message_log
            )
    

    如果 message_log.message_type 被索引。

    如果您没有这个表但想创建一个,并且 消息日志。消息类型 是索引的,使用递归 CTE 要模拟松散索引扫描:

    WITH    rows (message_type) AS
            (
            SELECT  MIN(message_type) AS mm
            FROM    message_log
            UNION ALL
            SELECT  message_type
            FROM    (
                    SELECT  mn.message_type, ROW_NUMBER() OVER (ORDER BY mn.message_type) AS rn
                    FROM    rows r
                    JOIN    message_type mn
                    ON      mn.message_type > r.message_type
                    WHERE   r.message_type IS NOT NULL
                    ) q
            WHERE   rn = 1
            )
    SELECT  message_type
    FROM    rows r
    OPTION (MAXRECURSION 0)
    
        4
  •  1
  •   Evan Carroll    16 年前

    我只想说明一个显而易见的问题:使数据正常化。

    message_types
    message_type | message_type_name
    
    messages
    message_id | message_type | message_type_name
    

    然后就可以不使用任何缓存的distinct:

    为了你的下拉列表

    SELECT * FROM message_types
    

    供您检索

    SELECT * FROM messages WHERE message_type = ? 
    
    SELECT m.*, mt.message_type_name FROM messages AS m
    JOIN message_types AS mt
    ON ( m.message_type = mt.message_type)
    

    我不知道你为什么要缓存 DISTINCT 如果可以的话,你必须更新 轻微地 调整模式,并有一个与ri。

        5
  •  1
  •   Ian Boyd    16 年前

    在消息类型上创建索引:

    CREATE INDEX IX_Messages_MessageType ON Messages (MessageType)
    

    然后得到一个唯一的列表 消息类型 你跑:

    SELECT DISTINCT MessageType
    FROM Messages
    ORDER BY MessageType
    

    因为索引是按照 消息类型 SQL Server可以非常快速地 有效地 ,扫描索引,获取唯一消息类型的列表。

    它的性能并不差——这正是sql server擅长的。


    不可否认,你可以节省一些空间 消息类型 “桌子。如果一次只显示几条消息: 书签查找 ,当它连接回 MessageTypes 桌子,没问题。但如果一次开始显示成百上千条消息,那么 消息类型 会变得非常昂贵,而且不必要,而且拥有 MessageType 与消息一起存储。

    但是我可以在 消息类型 列,并选择 distinct . sql server喜欢这样的东西。但是如果你发现这是你服务器上的一个真正的负载,一旦你每秒收到几十次点击,那么按照另一个建议,将它们缓存在内存中。

    我个人的解决方案是:

    • 创建索引
    • 选择非重复

    如果我还有问题

    • 缓存在30秒后过期的内存中

    关于规范化/非规范化问题。规范化可以节省空间,但在不断执行连接时会以cpu为代价。但非标准化的逻辑点是避免重复数据,这会导致数据不一致。

    是否计划更改消息类型的文本,如果与消息一起存储,则必须更新所有行?

    或者有什么要说的事实是,在消息的时候,消息类型 “请求客户端响应”?

        6
  •  0
  •   c. liau    16 年前

    你考虑过索引视图吗?它的结果集被具体化并持久化在存储中,这样查找的开销就与您要做的其他事情分离开来。

    当数据发生变化时,sql server负责自动更新视图,在它看来,这将改变视图的内容,因此在这方面它不如oracle具体化的灵活。

        7
  •  0
  •   Adriaan Stander    16 年前

    消息类型应该是主表中包含消息类型代码和说明的定义表的外键。这将大大提高查找性能。

    有点像

    DECLARE @MessageTypes TABLE(
            MessageTypeCode VARCHAR(10),
            MessageTypeDesciption VARCHAR(100)
    )
    
    DECLARE @Messages TABLE(
            MessageTypeCode VARCHAR(10),
            MessageValue VARCHAR(MAX),
            MessageLogDate DATETIME,
            AdditionalNotes VARCHAR(MAX)
    )
    

    从这个设计中,您的查找应该只查询 消息类型

        8
  •  0
  •   Jay    16 年前

    正如其他人所说,创建一个单独的消息类型表。向消息表中添加记录时,请检查表中是否已存在该消息类型。如果没有,添加它。在这两种情况下,然后将标识符从消息类型表发布到消息表中。这将为您提供规范化的数据。是的,当你添加一个记录的时候,这是一个额外的时间,但是在检索上应该更有效。

    如果有更多的add,然后读取,如果“消息类型”很短,一个完全不同的方法是仍然创建单独的消息类型表,但是在执行add时不要引用它,而是根据需要懒洋洋地更新它。

    即,(a)在每个消息记录中包括时间戳。(b)保留上次检查时发现的消息类型的列表。(c)每次检查时,搜索自上次添加的任何新邮件类型,如:

    create table temp_new_types as
        (select distinct message_type
        from message
        where timestamp>last_type_check
    );
    
    insert into message_type_list (message_type)
    select message_type
    from temp_new_types
    where message_type not in (select message_type from message_type_list);
    
    drop table temp_new_types;
    

    然后把这张支票的时间戳保存在某处,以便下次使用。

        9
  •  0
  •   user215054    16 年前

    答案是使用“distinct”,对于不同大小的表,每个最佳解决方案都是不同的。几千排,几百万,十亿?更多?这是非常不同的最佳解决方案。