繁体   English   中英

SQL Server 中的哈希表数据结构

[英]Hash Table Data Structure in SQL Server

最近几天,我一直在阅读一本关于数据结构的电子书,坦率地说,很多事情已经从我的脑海中消失了。 只是回顾它们并试图再次澄清。 我正在浏览哈希表并再次熟悉它。 所以我知道并听说,SQL Server 在内部使用哈希表,stackoverflow.com 和 forums.asp.net 的许多线程询问有关在 SQL Server 中创建哈希表的问题,因为它存储临时数据。 因此,让我举一个我在使用临时表的存储过程中使用的示例:(避免它,它太长了。仅作为示例)

第一:

CREATE PROCEDURE [dbo].[Orders]
    @OrderLine int
AS
BEGIN
    DECLARE @t1 TABLE(Date1 date, 
                      OrderID VARCHAR(MAX), 
                      EmployeeName VARCHAR(MAX), 
                      DeliveryDate date, 
                      StoreName VARCHAR(MAX),
                      DeliveryAddress VARCHAR(MAX), 
                      ItemName VARCHAR(MAX), 
                      Quantity FLOAT)

    INSERT INTO @t1(Date1, OrderID, EmployeeName, DeliveryDate, StoreName, DeliveryAddress, ItemName, Quantity)
        (SELECT DISTINCT 
             CONVERT(VARCHAR(11), DemandOrder.POCreationDate, 6) AS DemandOrderDate, 
             DemandOrder.OrderID, EmployeeDetails.EmployeeName,
             CONVERT(DATE, DemandOrder.DeliveryDate) AS ExpectedDeliveryDate, 
             StoreDetails.StoreName,
             DemandOrder.DeliveryAddress, Item.ItemName, 
             DemandOrderLine.Quantity 
         FROM 
             DemandOrder 
         INNER JOIN 
             DemandOrderLine ON DemandOrder.OrderID = DemandOrderLine.OrderID 
         INNER JOIN 
             Item on DemandOrderLine.ItemID=Item.ItemID 
         INNER JOIN 
             EmployeeDetails ON EmployeeDetails.EmployeeID = DemandOrder.EmployeeID 
         INNER JOIN 
             StoreDetails ON DemandOrderLine.StoreID = StoreDetails.StoreID
         WHERE 
             DemandOrderLine.OrderLine = @OrderLine)

    DECLARE @t2 TABLE(Approvedby VARCHAR(MAX)) 

    INSERT INTO @t2(Approvedby)
        (SELECT EmployeeDetails.EmployeeName 
         FROM EmployeeDetails 
         INNER JOIN DemandOrderLine ON DemandOrderLine.ApprovedBy = EmployeeDetails.EmployeeID)

    SELECT DISTINCT 
        CONVERT(VARCHAR(11), Date1, 6) AS Date, 
        OrderID, EmployeeName,
        CONVERT(VARCHAR(11), DeliveryDate, 6) AS ExpectedDeliveryDate, 
        StoreName, Approvedby, DeliveryAddress, 
        ItemName, Quantity  
    FROM 
        @t1 
    CROSS JOIN 
        @t2
END   

另一个,来自一个例子,它说在存储过程中,不能使用哈希表。 所以这里是:

第二:

CREATE PROCEDURE TempTable AS ---- It's actually not possible in SP

CREATE table #Color
(
    Color varchar(10) PRIMARY key
)

INSERT INTO #color 
    SELECT 'Red' 
    UNION 
    SELECT 'White'
    UNION 
    SELECT 'green'
    UNION 
    SELECT 'Yellow'
    UNION 
    SELECT 'blue'

DROP TABLE #color

CREATE table #Color
(
    Color varchar(10) PRIMARY key
)

INSERT INTO #color 
    SELECT 'Red' 
    UNION 
    SELECT 'White'
    UNION 
    SELECT 'green'
    UNION 
    SELECT 'Yellow'
    UNION 
    SELECT 'blue'

DROP TABLE #color
GO

所以我的问题是我可以说第一个是哈希表的一个例子,因为它使用临时表,如果不是,为什么我们不能在存储过程中使用它? 同样,如果它是内部创建的,为什么我们需要再次创建一个哈希表用于工作目的(虽然它有性能问题,只是想知道上面的例子是否适用于这个目的)。 谢谢。

注意:我上个月接受了一次采访并正在讨论它。 这就是为什么要确定我的观点是否正确。

这个评论太长了。

基于哈希的算法对于任何功能强大的数据库都很重要。 这些用于聚合和联接操作。 从7.0版开始,基于散列的联接就存在了-确实很旧(感谢Martin Smith)。 您可以在文档中阅读有关它们的更多信息。

SQL Server 2014引入了针对内存优化表的基于哈希的索引(请参见此处 )。 这些是哈希表的显式使用。 通常,基于树的索引功能更强大,因为它们可以在更多情况下使用:

  • 用于范围查找(包括like )。
  • 对于部分键匹配。
  • order by

哈希索引只能用于完全相等的匹配(和group by )。

我知道晚会晚了一点,但我认为没有人直接回答过您的原始问题。

第一个是表变量的示例,第二个是本地表的示例,两者均在tempdb中创建

它们之间的区别在于,表变量不是在内存中创建的,并且不能具有聚集索引。 同样,本地(哈希)表将一直停留到单个连接结束,而表变量仅可用于其声明所在的批处理。全局表(在其之前使用双哈希)将可用于所有连接并持续到使用它的所有连接均已关闭。

最后一件事,您不能在存储过程中使用该本地表的唯一原因是因为它使用了两次相同的名称,即使您已经使用了drop table,它也会首先基于批处理中的创建对其进行评估。 因此,它不会执行任何操作并且会抱怨它已经存在。

另请注意以下注意事项作为扩展注释,而不是单独的答案:

[1] SQL Server具有INNER HASH JOININNER HASH JOIN )和查询提示( OPTION (HASH JOIN)OPTION(HAS GROUP') ),以便实施此类物理联接或分组。

[2]在内部,SQL Server使用哈希表作为锁标识符。 请参见未记录的功能%% lockres %%(请参阅http://www.sqlskills.com/blogs/paul/investigating-locking-and-deadlocking-with-lockres/ )。

USE tempdb
GO

CREATE TABLE dbo.Documents (
    ID          INT IDENTITY(1,1) PRIMARY KEY,
    DocDate     DATE NOT NULL,
    Content     VARCHAR(8000) NOT NULL
);

CREATE INDEX IX_Documents_DocDate
ON dbo.Documents (DocDate)
GO

INSERT  dbo.Documents (DocDate, Content)
VALUES  
('2016-01-01', REPLICATE(CONVERT(VARCHAR(MAX), 'A'), 4000)),
('2016-02-02', REPLICATE(CONVERT(VARCHAR(MAX), 'B'), 5000)),
('2016-03-04', REPLICATE(CONVERT(VARCHAR(MAX), 'C'), 6000));
GO

SELECT  ID, DocDate, %%lockres%% AS record_lock_hash
FROM    dbo.Documents WITH(INDEX=1) -- Clustered index
GO
/*
ID          DocDate    record_lock_hash
----------- ---------- --------------------------------
1           2016-01-01 (8194443284a0)
2           2016-02-02 (61a06abd401c)
3           2016-03-04 (98ec012aa510)
*/
GO

SELECT  ID, DocDate, %%lockres%% AS record_lock_hash
FROM    dbo.Documents WITH(INDEX=IX_Documents_DocDate) -- Non-Clustered index
GO
/*
ID          DocDate    record_lock_hash
----------- ---------- --------------------------------
1           2016-01-01 (417e1de8c3fb)
2           2016-02-02 (6aede25aab61)
3           2016-03-04 (e701c6578164)
*/
GO

[3]作为数据库开发人员,我使用“哈希表”(使用哈希函数定义的计算列)来索引大文本(或blob),因此:

ALTER TABLE dbo.Documents 
ADD ContentHash AS CHECKSUM(HASHBYTES('sha256', Content)) /*PERSISTED*/ -- Computed column
GO

SET ANSI_NULLS, QUOTED_IDENTIFIER ON
GO
SET ANSI_WARNINGS, ANSI_PADDING, ARITHABORT, CONCAT_NULL_YIELDS_NULL ON
SET NUMERIC_ROUNDABORT OFF
GO

CREATE INDEX IX_Documents_ContentHash
ON dbo.Documents (ContentHash) 
--INCLUDE (Content)
GO

DECLARE @ParamContent VARCHAR(8000) = REPLICATE(CONVERT(VARCHAR(MAX), 'B'), 5000)

DECLARE @ParamHash INT = CHECKSUM(HASHBYTES('sha256', @ParamContent))
SELECT  d.ID, d.Content
FROM    dbo.Documents d WITH(FORCESEEK)
WHERE   d.ContentHash = @ParamHash  -- Index Seek Predicate (Fast/optimized if there is an index on ContentHash computed column)
AND     d.Content = @ParamContent   -- [Simple / non-indexed] Predicate (Slow)
GO

在此处输入图片说明

注意: 索引计算列时请注意 (请参阅SET tings的要求)。

DECLARE @SEPERATOR as VARCHAR(1)
    DECLARE @SP INT
    DECLARE @VALUE VARCHAR(MAX)
    SET @SEPERATOR = ','
    CREATE TABLE #TempCode (id int NOT NULL)
    /**this Region For Storing SiteCode**/
    WHILE PATINDEX('%' + @SEPERATOR + '%', @Code ) <> 0 
        BEGIN
                SELECT  @SP = PATINDEX('%' + @SEPERATOR + '%' ,@Code)
                SELECT  @VALUE = LEFT(@Code , @SP - 1)
                SELECT  @Code = STUFF(Code, 1, @SP, '') 
                INSERT INTO #TempCode (id) VALUES (@VALUE)
        END

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM