1

有没有办法更有效地生成一百万个 MAC 地址?

这是我正在做的事情,但是生成一百万个 MAC 地址并将它们插入到我的表中需要大约 10 分钟:

DECLARE @StartRange BINARY(6) 
DECLARE @EndRange BINARY(6) 
SET @StartRange = 0x0036D1F00000 
SET @EndRange = 0x0036D1FFFFFF 
--select convert(bigint,+ @EndRange) - convert(bigint,+ @StartRange) = 1048575
WHILE(convert(bigint, (SELECT IDENT_CURRENT('Mac_Address'))) < (convert(bigint, @EndRange) - convert(bigint, @StartRange)))
BEGIN
insert into Mac_Address (MacAddress)
select convert(BINARY(6),(convert(bigint, (SELECT IDENT_CURRENT('Mac_Address'))) + convert(bigint, @StartRange)))
END

这是我在网上找到的执行此操作的代码,它会在几秒钟内执行。但是它正在将 MAC 地址生成到系统表中:

DECLARE @StartRange BINARY(8) 
DECLARE @EndRange BINARY(8) 
SET @StartRange = 0x00000004A500114B 
SET @EndRange = 0x00000004A50F11FF 

--select convert(integer,+ @EndRange) - convert(integer,+ @StartRange) = 983220

select convert(BINARY(8),RW + convert(integer, @StartRange)) 
from
(select row_number() over(order by a.id) As RW from syscolumns,syscolumns a,syscolumns b ,syscolumns c) b --I do not understand this line very well.
where RW between 1 and (convert(integer, @EndRange) - convert(integer, @StartRange))
4

4 回答 4

4

正如您所说,这不是“将 MAC 地址生成到系统表中”。它只是使用syscolumns表作为获取大量行的一种手段。您可以知道这一点,因为所有系统表都在该FROM子句中。要插入到表中,至少需要INSERTor SELECT ... INTO

它速度快的原因是因为 SQL Server 针对“基于行”的操作进行了优化。在引擎内部,当然它必须遍历每一行,但它已经过大量优化。当您将 T-SQL 视为一种过程语言并一次循环遍历每个项目时,它不能使用这些优化,并且必须在完全不同的执行上下文中一次执行每个语句。

syscolumns本身毫无意义。它具有价值,因为它保证有很多行。它可以是具有许多行的任何表——FROM子句中的表只是被交叉连接在一起以获得它们的笛卡尔积(意味着很多行),因此该Row_Number()函数可以使用一些东西来创建一个包含来自的数字的行集1 到 1,000,000。

如果您愿意,您可以使用这样的方法,它根本不会碰到任何“真实”表,它的执行速度应该一样快或更快。它所做的只是获取初始SELECT 1 UNION ALL SELECT 1值并通过重复CROSS JOINing 将其转换为 43 亿行(但使用的旧式语法FROM Table1, Table2与 as 相同FROM Table1 CROSS JOIN Table2但更短)。

我在 SQL 2008 中测试了以下内容,它运行良好。在我的 SQL Server 上需要3 秒(此时 SQL Fiddle 错误地抛出错误,我已通过电子邮件发送给管理员)。

DECLARE
   @StartRange bigint = 0x0004A500114B,
   @EndRange bigint = 0x0004A50F11FF;

WITH -- make sure you terminate your prior statement by putting ; after it
L0 AS (SELECT 1 N UNION ALL SELECT 1),
L1 AS (SELECT 1 N FROM L0, L0 B),
L2 AS (SELECT 1 N FROM L1, L1 B),
L3 AS (SELECT 1 N FROM L2, L2 B),
L4 AS (SELECT 1 N FROM L3, L3 B),
L5 AS (SELECT 1 N FROM L4, L4 B),
Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) N FROM L5)
INSERT Mac_Address (MacAddress)
SELECT Convert(binary(6), N + @StartRange - 1)
FROM Nums
WHERE N <= @EndRange - @StartRange + 1

但是,我不能 100% 确定这将在 SQL Server 2012 的 Express 版本中工作。使用该syscolumns版本实际上没有任何问题,您只需添加一条INSERT语句并更正一些问题(包括int而不是bigintbinary(8)而不是binary(6)) . 请注意,您可以通过WITH (NOLOCK)在每个syscolumns表之后添加(或SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED在查询之前和SET TRANSACTION ISOLATION LEVEL READ COMMITTED之后使用)来提高性能。这个查询也执行得很好(在我的服务器上 2 秒)。

DECLARE
   @StartRange bigint = 0x0004A500114B,
   @EndRange bigint = 0x0004A50F11FF;

INSERT Mac_Address (MacAddress) -- Add this line
SELECT Convert(binary(6), N + @StartRange - 1) 
FROM
(
    SELECT N = Row_Number() OVER (ORDER BY (SELECT 1))
    FROM
       syscolumns WITH (NOLOCK),
       syscolumns a WITH (NOLOCK),
       syscolumns b WITH (NOLOCK),
       syscolumns c WITH (NOLOCK)
) b
WHERE N <= @EndRange - @StartRange + 1;
于 2012-12-28T23:04:19.633 回答
1

第二个片段根本没有插入任何东西。它仅使用 syscolumns 表的(与自身交叉连接)行号来生成整数。

您可以为此使用任何其他表(前提是您有足够的行数或加入足够的次数来生成它)

于 2012-12-28T23:01:58.833 回答
1

它并没有真正使用 syscolumns。它只是将它连接在一起以创建一个包含足够行数的表以供您的范围使用。如果您对此感到不舒服,可以执行以下操作:

with digits as (select 0 as dig union all
                select 1 union all
                select 2 union all
                select 3 union all
                select 4 union all
                select 5 union all
                select 6 union all
                select 7 union all
                select 8 union all
                select 9
               ),
      nums as (select dig1 + 10*dig2+100*dig3+1000*dig4+10000*dig5+100000*dig6 as num
               from dig dig1 cross join dig dig2 cross join
                    dig dig3 cross join dig dig4 cross join
                    dig dig5 cross join dig dig6
              )
select convert(BINARY(8), num + convert(integer, @StartRange)) 
from nums
where num + 1 between 1 and (convert(integer, @EndRange) - convert(integer, @StartRange))
于 2012-12-28T23:04:22.887 回答
1

以下内容很简单,并且在在线SqlFiddle上运行了大约一分钟:

DECLARE @StartRange bigint;
DECLARE @EndRange bigint;
SET @StartRange = 0x0036D1F00000;
SET @EndRange =   0x0036D1FFFFFF; 

WITH cteMacAddress AS (
  SELECT @StartRange MacAddress
  UNION ALL
  SELECT MacAddress+1 FROM cteMacAddress
  WHERE MacAddress <= @EndRange
)
INSERT Mac_Address
  SELECT CAST(MacAddress AS BINARY(6))
  FROM cteMacAddress
  OPTION (MAXRECURSION 0);

SELECT COUNT(*) GeneratedAddressCount
  FROM Mac_Address;
于 2012-12-28T23:24:55.977 回答