SQL Server中的哈希表数据结构

时间:2017-01-01 13:25:48

标签: sql sql-server sql-server-2008 stored-procedures hashtable

在过去的几天里,我一直在阅读一本关于数据结构的电子书,坦率地说,很多事情已经从我的头脑中消失了。只是审查它们并试图再次澄清。我正在浏览哈希表并再次熟悉它。所以我知道并且听说过,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

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

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

4 个答案:

答案 0 :(得分:5)

评论太长了。

基于哈希的算法对于任何强大的数据库都很重要。这些用于聚合和连接操作。自7.0版本以来,基于哈希的连接已经存在 - 这真的很旧(感谢Martin Smith)。您可以在documentation

中详细了解它们

SQL Server 2014为内存优化表引入了基于哈希的索引(请参阅here)。这些是哈希表的显式使用。但是,一般而言,基于树的索引更强大,因为它们可以在更多情况下使用:

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

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

答案 1 :(得分:2)

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

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

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

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

答案 2 :(得分:1)

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

[1] SQL Server已加入(INNER HASH JOIN)和查询提示(OPTION (HASH JOIN)OPTION(HAS GROUP')),以强制执行此类物理连接或分组。

[2]在内部,SQL Server使用哈希表作为锁标识符。请参阅未记录的函数%% lockres %%(ref。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

enter image description here

注意:indexing computed columns时请注意(请参阅SET的要求)。

答案 3 :(得分:0)

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