Hum*_*All 7 database-design sql-server hierarchy sql-server-2014
记录数据的表是
CREATE TABLE [dbo].[Almoxarifado](
[idAlmoxarifado] [varchar](20) NOT NULL,
[tipoAlmoxarifadoId] [varchar](30) NOT NULL,
[entidadeId] [bigint] NOT NULL,
[dtInclusao] [smalldatetime] NOT NULL,
[dtUltimaAlteracao] [smalldatetime] NULL,
[descricao] [varchar](255) NOT NULL,
[terceiro] [bit] NOT NULL
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
这是一个SELECT TOP 10 *
示例(该表有超过 100 万个条目):
如您所见,该idAlmoxarifado
字段存储的分层数据与大多数分层表没有分层关系。
现在我想把这些数据带到一个通常的层次表中:
CREATE TABLE [dbo].[Almoxarifado2](
[idMaster] [bigint] IDENTITY(1,1) NOT NULL,
[idAlmoxarifado] [int] NOT NULL,
[idAlmoxPai] [int] NOT NULL DEFAULT ((0)),
[entidadeId] [bigint] NOT NULL,
[tipoAlmoxarifadoId] [varchar](30) NOT NULL
[dtInclusao] [datetime] NULL DEFAULT (getdate()),
[dtUltimaAlteracao] [datetime] NULL,
[descricao] [varchar](255) NOT NULL,
[terceiro] [bit] NULL DEFAULT ((0)),
PRIMARY KEY ([idMaster])
)
Run Code Online (Sandbox Code Playgroud)
在这张表上,我将有一个通常的父子关系,其中给定字段的“父亲”将始终是它的idMaster
通讯员(除非它位于层次链的第一级;那么它的“父亲”将是0
)。
所以,如果我们按照上图所示的相同顺序,我们应该有(在新表上插入转置数据后):
由于原始表有超过 1 英里的条目,我如何才能安全无痛地进行这种换位?
可以存在多少个顶级没有限制;两个孩子都没有水平。我们的想法是使用掩码使数据对我们的应用程序有用(我们使用摩托罗拉手持计算机;检查 MC45 和 MC3190)。假设购买我们解决方案的客户有几个仓库,他在那里存放他的产品。在这种情况下,他将能够定义一个掩码(比如 9.99.99.999 或 9.9.99.99.99.999),我们的系统必须能够使用这个掩码来允许他在这些级别注册他的仓库。
所以你基本上可以说我们的系统以某种方式专注于物流。
第一层可以是“NY South Warehouse”、第二层“Room #2”、第三层“Corridor A”、第四层“Locker 2”和第五层“Shelf 3”。
wBo*_*Bob 10
由于您的数据看起来有点像hierarchyId,我考虑过使用它。初始版本不能很好地扩展到 100 万行,但是主临时表上的一些索引选择有所帮助。然而,问题也可能是由于我的测试数据造成的,所以你能告诉我更多关于你的层次结构的信息吗?比如有多少顶级父母,平均有多少个级别,每个级别可以有多少个孩子?
同时,这里有一个版本,您可以浏览一下,看看是否hierarchyId
适合您。
基本思想是创建一个临时表,将原始 id 转换为hierarchyIds
,然后遍历层次结构以计算出世系。这个临时表(可能是永久的)然后可以用于迁移:
USE tempdb
GO
SET NOCOUNT ON
GO
IF OBJECT_ID('[dbo].[Almoxarifado]') IS NOT NULL DROP TABLE [dbo].[Almoxarifado]
IF OBJECT_ID('[dbo].[Almoxarifado2]') IS NOT NULL DROP TABLE [dbo].[Almoxarifado2]
GO
CREATE TABLE [dbo].[Almoxarifado](
[idAlmoxarifado] [varchar](20) NOT NULL,
[tipoAlmoxarifadoId] [varchar](30) NOT NULL,
[entidadeId] [bigint] NOT NULL,
[dtInclusao] [smalldatetime] NOT NULL,
[dtUltimaAlteracao] [smalldatetime] NULL,
[descricao] [varchar](255) NOT NULL,
[terceiro] [bit] NOT NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Almoxarifado2](
[idMaster] [bigint] IDENTITY(1,1) NOT NULL,
[idAlmoxarifado] [int] NOT NULL,
[idAlmoxPai] [int] NOT NULL DEFAULT ((0)),
[entidadeId] [bigint] NOT NULL,
[tipoAlmoxarifadoId] [varchar](30) NOT NULL ,
[dtInclusao] [datetime] NULL DEFAULT (getdate()),
[dtUltimaAlteracao] [datetime] NULL,
[descricao] [varchar](255) NOT NULL,
[terceiro] [bit] NULL DEFAULT ((0)),
PRIMARY KEY ([idMaster])
)
GO
/*
-- Test data
INSERT INTO [dbo].[Almoxarifado] ( idAlmoxarifado, tipoAlmoxarifadoId, entidadeId, dtInclusao, dtUltimaAlteracao, descricao, terceiro )
VALUES
( '1', 'TESTE', 12, '29 Apr 2016', NULL, '0000000', 0 ),
( '1.1', 'TESTE', 12, '29 Apr 2016', NULL, '0000001', 0 ),
( '1.1.01', 'TESTE', 12, '29 Apr 2016', NULL, '0000002', 0 ),
( '1.1.01.01', 'TESTE', 12, '29 Apr 2016', NULL, '0000003', 0 ),
( '1.1.01.01.001', 'TESTE', 12, '29 Apr 2016', NULL, '0000004', 0 ),
( '1.1.01.01.002', 'TESTE', 12, '29 Apr 2016', NULL, '0000005', 0 ),
( '1.1.01.01.003', 'TESTE', 12, '29 Apr 2016', NULL, '0000006', 0 ),
( '1.1.01.01.004', 'TESTE', 12, '29 Apr 2016', NULL, '0000007', 0 ),
( '1.1.01.01.005', 'TESTE', 12, '29 Apr 2016', NULL, '0000008', 0 ),
( '1.1.01.01.006', 'TESTE', 12, '29 Apr 2016', NULL, '0000009', 0 )
GO
*/
-- Add 10 parent levels
INSERT INTO [dbo].[Almoxarifado] ( idAlmoxarifado, tipoAlmoxarifadoId, entidadeId, dtInclusao, dtUltimaAlteracao, descricao, terceiro )
VALUES
( '1', 'TESTE', 1, '1 Jan 2016', NULL, '00000001', 0 ),
( '2', 'TESTE', 1, '1 Feb 2016', NULL, '00000002', 0 ),
( '3', 'TESTE', 1, '1 Mar 2016', NULL, '00000003', 0 ),
( '4', 'TESTE', 1, '1 Apr 2016', NULL, '00000004', 0 ),
( '5', 'TESTE', 1, '1 May 2016', NULL, '00000005', 0 ),
( '6', 'TESTE', 1, '1 Jun 2016', NULL, '00000006', 0 ),
( '7', 'TESTE', 1, '1 Jul 2016', NULL, '00000007', 0 ),
( '8', 'TESTE', 1, '1 Aug 2016', NULL, '00000008', 0 ),
( '9', 'TESTE', 1, '1 Sep 2016', NULL, '00000008', 0 ),
( '10', 'TESTE', 1, '1 Oct 2016', NULL, '00000010', 0 )
-- For each parent, add 3 sublevels
INSERT INTO [dbo].[Almoxarifado] ( idAlmoxarifado, tipoAlmoxarifadoId, entidadeId, dtInclusao, dtUltimaAlteracao, descricao, terceiro )
SELECT
idAlmoxarifado + '.' + CAST( x.y AS VARCHAR(10) ),
tipoAlmoxarifadoId, entidadeId, dtInclusao, dtUltimaAlteracao, descricao, terceiro
FROM [dbo].[Almoxarifado] a
CROSS JOIN ( SELECT TOP 5 idAlmoxarifado y FROM [dbo].[Almoxarifado] ) x
-- add n sublevels
INSERT INTO [dbo].[Almoxarifado] ( idAlmoxarifado, tipoAlmoxarifadoId, entidadeId, dtInclusao, dtUltimaAlteracao, descricao, terceiro )
SELECT
idAlmoxarifado + '.0' + CAST( x.y AS VARCHAR(10) ),
tipoAlmoxarifadoId, entidadeId, dtInclusao, dtUltimaAlteracao, descricao, terceiro
FROM [dbo].[Almoxarifado] a
CROSS JOIN ( SELECT TOP 8 idAlmoxarifado y FROM [dbo].[Almoxarifado] WHERE idAlmoxarifado Not Like '%.%' ) x
WHERE idAlmoxarifado Like '%.%'
-- Add 8 sublevels
INSERT INTO [dbo].[Almoxarifado] ( idAlmoxarifado, tipoAlmoxarifadoId, entidadeId, dtInclusao, dtUltimaAlteracao, descricao, terceiro )
SELECT
idAlmoxarifado + '.0' + CAST( x.y AS VARCHAR(10) ),
tipoAlmoxarifadoId, entidadeId, dtInclusao, dtUltimaAlteracao, descricao, terceiro
FROM [dbo].[Almoxarifado] a
CROSS JOIN ( SELECT TOP 13 ROW_NUMBER() OVER( ORDER BY idAlmoxarifado ) y FROM [dbo].[Almoxarifado] ) x
WHERE idAlmoxarifado Like '%.%.%'
-- Add 9 sublevels
INSERT INTO [dbo].[Almoxarifado] ( idAlmoxarifado, tipoAlmoxarifadoId, entidadeId, dtInclusao, dtUltimaAlteracao, descricao, terceiro )
SELECT
idAlmoxarifado + '.0' + CAST( x.y AS VARCHAR(10) ),
tipoAlmoxarifadoId, entidadeId, dtInclusao, dtUltimaAlteracao, descricao, terceiro
FROM [dbo].[Almoxarifado] a
CROSS JOIN ( SELECT TOP 21 ROW_NUMBER() OVER( ORDER BY idAlmoxarifado ) y FROM [dbo].[Almoxarifado] ) x
WHERE idAlmoxarifado Like '%.%.%.%'
GO
INSERT INTO [dbo].[Almoxarifado] ( idAlmoxarifado, tipoAlmoxarifadoId, entidadeId, dtInclusao, dtUltimaAlteracao, descricao, terceiro )
SELECT
idAlmoxarifado + '.0' + CAST( x.y AS VARCHAR(10) ),
tipoAlmoxarifadoId, entidadeId, dtInclusao, dtUltimaAlteracao, descricao, terceiro
FROM [dbo].[Almoxarifado] a
CROSS JOIN ( SELECT TOP 9 ROW_NUMBER() OVER( ORDER BY idAlmoxarifado ) y FROM [dbo].[Almoxarifado] ) x
WHERE idAlmoxarifado Like '%.%.%.%.%'
GO
-- Main hierarchyId processing
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL DROP TABLE #tmp
SELECT
IDENTITY( INT, 1, 1 ) AS idMaster,
idAlmoxarifado,
CAST( '/' + REPLACE( REPLACE( REPLACE( idAlmoxarifado, '.0', '.' ), '.0', '.' ), '.', '/' ) + '/' AS hierarchyid ) hId,
CAST( '/' + REPLACE( REPLACE( REPLACE( idAlmoxarifado, '.0', '.' ), '.0', '.' ), '.', '/' ) + '/' AS hierarchyid ).ToString() hString,
CAST( NULL AS INT ) AS idAlmoxarifado2,
CAST( NULL AS INT ) AS idAlmoxPai
INTO #tmp
FROM [dbo].[Almoxarifado]
GO
-- Index temp table to help with recursive CTE
ALTER TABLE #tmp ADD PRIMARY KEY ( idMaster )
ALTER TABLE #tmp ALTER COLUMN hId hierarchyId NOT NULL
CREATE UNIQUE INDEX _idx ON #tmp ( hId ) INCLUDE ( idAlmoxarifado, hString )
GO
-- Walk the hierarchy
;WITH cte AS (
SELECT 1 AS xlevel, idMaster, idAlmoxarifado, hId, hString, hId.GetLevel() getLevel, 0 AS parentId
FROM #tmp
WHERE hId.GetLevel() = 1
UNION ALL
SELECT xlevel + 1, t.idMaster, t.idAlmoxarifado, t.hId, t.hString, t.hId.GetLevel() getLevel, c.idMaster AS parentId
FROM cte c
INNER JOIN #tmp t ON c.hId = t.hId.GetAncestor(1)
)
UPDATE t
SET
-- Parse out the individual node, by taking its parent and stuffing it in front
t.idAlmoxarifado2 = CAST( REPLACE( STUFF( c.hString, 1, LEN(c.hId.GetAncestor(1).ToString()), '' ), '/', '' ) AS INT ),
t.idAlmoxPai = parentId
FROM cte c
INNER JOIN #tmp t ON c.idMaster = T.idMaster
-- Check results
SELECT *
FROM #tmp
Run Code Online (Sandbox Code Playgroud)
整个脚本(包括测试数据创建)在我的笔记本电脑上只需几分钟即可运行。
添加一列dbo.Almoxarifado2
将保存 的原始值idAlmoxarifado
。
添加所有行dbo.Almoxarifado
,然后用于idAlmoxarifadoOrg
更新父行idAlmoxPai
的值idMaster
。
带有一些注释的代码:
-- Add a column to store original idAlmoxarifado
alter table dbo.Almoxarifado2
add idAlmoxarifadoOrg varchar(20) not null;
go
-- Add all rows to Almoxarifado2 with 0 to idAlmoxPai
-- and the rightmost number of idAlmoxarifado to idAlmoxarifado.
-- Original value of idAlmoxarifado goes to idAlmoxarifadoOrg
insert into dbo.Almoxarifado2(idAlmoxarifado, idAlmoxPai, entidadeId, tipoAlmoxarifadoId, dtInclusao, dtUltimaAlteracao, descricao, terceiro, idAlmoxarifadoOrg)
select right(A.idAlmoxarifado, charindex('.', reverse('.'+A.idAlmoxarifado)) - 1), 0, A.entidadeId, A.tipoAlmoxarifadoId, A.dtInclusao, A.dtUltimaAlteracao, A.descricao, A.terceiro, A.idAlmoxarifado
from dbo.Almoxarifado as A;
go
-- Update idAlmoxPai with the parent idMaster
-- Don't update root nodes
update A
set idAlmoxPai = P.idMaster
from dbo.Almoxarifado2 as A
inner join dbo.Almoxarifado2 as P
on P.idAlmoxarifadoOrg = left(A.idAlmoxarifadoOrg, len(A.idAlmoxarifadoOrg) - charindex('.', reverse(A.idAlmoxarifadoOrg)))
where charindex('.', A.idAlmoxarifadoOrg) > 0;
go
-- Cleanup
alter table dbo.Almoxarifado2 drop column idAlmoxarifadoOrg;
Run Code Online (Sandbox Code Playgroud)
有些部分可以处理更多信息。
这将right(A.idAlmoxarifado, charindex('.', reverse('.'+A.idAlmoxarifado)) - 1)
提取中的最后一个数字idAlmoxarifado
。所以1.1.10
会给你一个10
。
这部分left(A.idAlmoxarifado, len(A.idAlmoxarifado) - charindex('.', reverse(A.idAlmoxarifado)))
返回除最后一个数字之外的所有数字。因为1.1.10
它会返回1.1
。
我无耻地窃取了 wBob 生成的测试数据(感谢并非常感谢)并发现这个版本在我的机器上更快。我使用非常古老的 2 核笔记本电脑进行测试,因此在真实服务器上的结果可能会有所不同。无论如何,对我来说,wBob 执行代码需要 3 分钟,而我的解决方案是大约 20 秒。
归档时间: |
|
查看次数: |
227 次 |
最近记录: |