2009-09-08 6 views
50

J'ai vu de nombreuses façons de créer et de remplir un tableau de nombres. Cependant, quelle est la meilleure façon de créer et de peupler un? Avec « meilleur » étant défini de plus au moins important:Quelle est la meilleure façon de créer et de remplir une table de nombres?

  • Tableau créé avec l'indexation optimale
  • lignes générées le plus rapide
  • simple code utilisé pour créer et

Si vous n'êtes pas savoir ce qu'est un tableau de nombres, regardez ici: Why should I consider using an auxiliary numbers table?

+2

il s'est avéré que c'était plus ou moins un doublon de http://stackoverflow.com/questions/10819/sql-auxiliary-table-of-numbers, autant que je sache – Tao

+0

Ce lien mène maintenant à faux les pages d'alerte virale. –

Répondre

107

Voici quelques exemples de code tirés du Web et des réponses à cette question.

Pour chaque méthode, j'ai modifié le code d'origine afin que chacun utilise la même table et la même colonne: NumbersTest et Number, avec 10 000 lignes ou le plus près possible de ce code. De plus, j'ai fourni des liens vers le lieu d'origine.

MÉTHODE 1 ici est une méthode de mise en boucle très lent de here
moyenne 13,01 secondes
couru 3 fois éliminés le plus, voici les temps en secondes: 12,42, 13,60

DROP TABLE NumbersTest 
DECLARE @RunDate datetime 
SET @RunDate=GETDATE() 
CREATE TABLE NumbersTest(Number INT IDENTITY(1,1)) 
SET NOCOUNT ON 
WHILE COALESCE(SCOPE_IDENTITY(), 0) < 100000 
BEGIN 
    INSERT dbo.NumbersTest DEFAULT VALUES 
END 
SET NOCOUNT OFF 
-- Add a primary key/clustered index to the numbers table 
ALTER TABLE NumbersTest ADD CONSTRAINT PK_NumbersTest PRIMARY KEY CLUSTERED (Number) 
PRINT CONVERT(varchar(20),datediff(ms,@RunDate,GETDATE())/1000.0)+' seconds' 
SELECT COUNT(*) FROM NumbersTest 

MÉTHODE 2 voici un beaucoup plus rapide en boucle un de here
moy 1.1658 secondes
a couru 11 fois enlevé le plus haut, voici temps en secondes: 1.117, 1.140, 1.203, 1.170, 1.173, 1.156, 1.203, 1.153, 1.173, 1,170

DROP TABLE NumbersTest 
DECLARE @RunDate datetime 
SET @RunDate=GETDATE() 
CREATE TABLE NumbersTest (Number INT NOT NULL); 
DECLARE @i INT; 
SELECT @i = 1; 
SET NOCOUNT ON 
WHILE @i <= 10000 
BEGIN 
    INSERT INTO dbo.NumbersTest(Number) VALUES (@i); 
    SELECT @i = @i + 1; 
END; 
SET NOCOUNT OFF 
ALTER TABLE NumbersTest ADD CONSTRAINT PK_NumbersTest PRIMARY KEY CLUSTERED (Number) 
PRINT CONVERT(varchar(20),datediff(ms,@RunDate,GETDATE())/1000.0)+' seconds' 
SELECT COUNT(*) FROM NumbersTest 

MÉTHODE 3 Voici un seul INSERT basé sur le code de here
moyenne 488,6 millisecondes
a couru 11 fois éliminés le plus, voici les temps en millisecondes: 686, 673, 623, 686.343.343.376.360.343.453

DROP TABLE NumbersTest 
DECLARE @RunDate datetime 
SET @RunDate=GETDATE() 
CREATE TABLE NumbersTest (Number int not null) 
;WITH Nums(Number) AS 
(SELECT 1 AS Number 
UNION ALL 
SELECT Number+1 FROM Nums where Number<10000 
) 
insert into NumbersTest(Number) 
    select Number from Nums option(maxrecursion 10000) 
ALTER TABLE NumbersTest ADD CONSTRAINT PK_NumbersTest PRIMARY KEY CLUSTERED (Number) 
PRINT CONVERT(varchar(20),datediff(ms,@RunDate,GETDATE()))+' milliseconds' 
SELECT COUNT(*) FROM NumbersTest 

MÉTHODE 4 ici est un « semi-lo oping "méthode de here moy 348.3 millisecondes (il était difficile d'obtenir un bon timing en raison de la" GO "au milieu du code, toutes les suggestions seraient appréciées)
a couru 11 fois enlevé le plus haut, voici les temps en millisecondes: 356, 360, 283, 346, 360, 376, 326, 373, 330, 373

DROP TABLE NumbersTest 
DROP TABLE #RunDate 
CREATE TABLE #RunDate (RunDate datetime) 
INSERT INTO #RunDate VALUES(GETDATE()) 
CREATE TABLE NumbersTest (Number int NOT NULL); 
INSERT NumbersTest values (1); 
GO --required 
INSERT NumbersTest SELECT Number + (SELECT COUNT(*) FROM NumbersTest) FROM NumbersTest 
GO 14 --will create 16384 total rows 
ALTER TABLE NumbersTest ADD CONSTRAINT PK_NumbersTest PRIMARY KEY CLUSTERED (Number) 
SELECT CONVERT(varchar(20),datediff(ms,RunDate,GETDATE()))+' milliseconds' FROM #RunDate 
SELECT COUNT(*) FROM NumbersTest 

MÉTHODE 5 ici est un INSERT de Philip Kelley's answer
moyenne 92,7 millisecondes
couru 11 fois enlevé le plus élevé, Voici les temps en millisecondes: 80, 96, 96, 93, 110, 110, 80, 76, 93, 93

DROP TABLE NumbersTest 
DECLARE @RunDate datetime 
SET @RunDate=GETDATE() 
CREATE TABLE NumbersTest (Number int not null) 
;WITH 
    Pass0 as (select 1 as C union all select 1), --2 rows 
    Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows 
    Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows 
    Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows 
    Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows 
    --I removed Pass5, since I'm only populating the Numbers table to 10,000 
    Tally as (select row_number() over(order by C) as Number from Pass4) 
INSERT NumbersTest 
     (Number) 
    SELECT Number 
     FROM Tally 
     WHERE Number <= 10000 
ALTER TABLE NumbersTest ADD CONSTRAINT PK_NumbersTest PRIMARY KEY CLUSTERED (Number) 
PRINT CONVERT(varchar(20),datediff(ms,@RunDate,GETDATE()))+' milliseconds' 
SELECT COUNT(*) FROM NumbersTest 

MÉTHODE 6 ici est un INSERT de Mladen Prajdic answer
moyenne 82,3 millisecondes
couru 11 fois éliminés le plus, voici les temps en millisecondes: 80, 80, 93, 76, 93, 63, 93 , 76, 93, 76

DROP TABLE NumbersTest 
DECLARE @RunDate datetime 
SET @RunDate=GETDATE() 
CREATE TABLE NumbersTest (Number int not null) 
INSERT INTO NumbersTest(Number) 
SELECT TOP 10000 row_number() over(order by t1.number) as N 
FROM master..spt_values t1 
    CROSS JOIN master..spt_values t2 
ALTER TABLE NumbersTest ADD CONSTRAINT PK_NumbersTest PRIMARY KEY CLUSTERED (Number); 
PRINT CONVERT(varchar(20),datediff(ms,@RunDate,GETDATE()))+' milliseconds' 
SELECT COUNT(*) FROM NumbersTest 

PROCÉDÉ 7 ici est un insert unique basé sur le code de here
moyen 56,3 Millis econdes
a couru 11 fois éliminés le plus, voici les temps en millisecondes: 63, 50, 63, 46, 60, 63, 63, 46, 63, 46

DROP TABLE NumbersTest 
DECLARE @RunDate datetime 
SET @RunDate=GETDATE() 
SELECT TOP 10000 IDENTITY(int,1,1) AS Number 
    INTO NumbersTest 
    FROM sys.objects s1  --use sys.columns if you don't get enough rows returned to generate all the numbers you need 
    CROSS JOIN sys.objects s2 --use sys.columns if you don't get enough rows returned to generate all the numbers you need 
ALTER TABLE NumbersTest ADD CONSTRAINT PK_NumbersTest PRIMARY KEY CLUSTERED (Number) 
PRINT CONVERT(varchar(20),datediff(ms,@RunDate,GETDATE()))+' milliseconds' 
SELECT COUNT(*) FROM NumbersTest 

Après avoir examiné toutes ces méthodes, je vraiment comme la méthode 7, qui était la plus rapide et le code est assez simple aussi.

+0

Vu ce post des années plus tard. Je serais intéressé par le calendrier pour 1 million de lignes ou plus. J'essaierai peut-être un jour, mais 10000 sont probablement autant que cela serait raisonnablement nécessaire. –

+11

Bien qu'intéressant, le timing ne me semble pas important. Plus précisément parce que si jamais j'ai besoin d'une table de nombres, je vais le créer une fois et l'utiliser encore et encore. –

+0

Merci beaucoup! Je sais que c'est vieux, mais pour ceux qui débarquent ici, je suggèrerais de créer un tableau de nombres de 100 000 pour que vous puissiez l'utiliser en combinaison avec des dates. – BJury

2

J'utilise des tables de nombres pour principalement simuler des rapports dans BIRT sans avoir à jouer avec la création dynamique des jeux d'enregistrements. Je fais la même chose avec les dates, ayant une table s'étendant de 10 ans dans le passé à 10 ans dans le futur (et les heures de la journée pour le reportage plus détaillé). C'est une astuce pour être en mesure d'obtenir des valeurs pour toutes les dates même si vos «vraies» tables de données n'ont pas de données pour eux.

J'ai un script que j'utiliser pour créer ceux-ci, quelque chose comme (ce qui est de la mémoire):

drop table numbers; commit; 
create table numbers (n integer primary key); commit; 
insert into numbers values (0); commit; 
insert into numbers select n+1 from numbers; commit; 
insert into numbers select n+2 from numbers; commit; 
insert into numbers select n+4 from numbers; commit; 
insert into numbers select n+8 from numbers; commit; 
insert into numbers select n+16 from numbers; commit; 
insert into numbers select n+32 from numbers; commit; 
insert into numbers select n+64 from numbers; commit; 

Le nombre de lignes double à chaque ligne de sorte qu'il ne prend pas beaucoup pour produire vraiment d'énormes tables.

Je ne suis pas sûr d'être d'accord avec vous sur le fait qu'il est important d'être créé rapidement puisque vous ne le créez qu'une seule fois. Le coût de cet amortissement est amorti sur tous les accès, ce qui rend le temps relativement insignifiant.

+0

chaque _commits; _ résulte en _Msg 3902, niveau 16, état 1, ligne 1 La requête COMMIT TRANSACTION n'a pas de BEGIN TRANSACTION correspondante. –

+0

seulement 128 lignes sont insérées –

+0

@KM, le premier point est facilement résolu en débutant une transaction (DB/2, mon SGBD de prédilection, est généralement configuré pour les transactions de démarrage automatique). Et, si vous voulez plus de lignes, il vous suffit d'ajouter plus d'insertions. Chacun double la gamme de sorte qu'il est très facile d'obtenir de grands nombres si vous le souhaitez. Je préfère également donner des solutions SQL génériques lorsque cela est possible plutôt que de limiter les solutions à des fournisseurs spécifiques. – paxdiablo

34

-je utiliser ce qui est rapide comme l'enfer:

insert into Numbers(N) 
select top 1000000 row_number() over(order by t1.number) as N 
from master..spt_values t1 
     cross join master..spt_values t2 
+0

Eh bien maintenant, c'est incroyable. Merci! – BelgoCanadian

9

Je commence par le modèle suivant, qui est dérivé de nombreuses impressions de routine de Itzik Ben-Gan:

;WITH 
    Pass0 as (select 1 as C union all select 1), --2 rows 
    Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows 
    Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows 
    Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows 
    Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows 
    Pass5 as (select 1 as C from Pass4 as A, Pass4 as B),--4,294,967,296 rows 
    Tally as (select row_number() over(order by C) as Number from Pass5) 
select Number from Tally where Number <= 1000000 

Le « OÙ N < = La clause "1000000" limite la sortie à 1 à 1 million, et peut facilement être ajustée à la plage désirée.

Depuis c'est une clause WITH, elle peut être exploitée dans un INSERT ... SELECT ... comme ceci:

-- Sample use: create one million rows 
CREATE TABLE dbo.Example (ExampleId int not null) 

DECLARE @RowsToCreate int 
SET @RowsToCreate = 1000000 

-- "Table of numbers" data generator, as per Itzik Ben-Gan (from multiple sources) 
;WITH 
    Pass0 as (select 1 as C union all select 1), --2 rows 
    Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows 
    Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows 
    Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows 
    Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows 
    Pass5 as (select 1 as C from Pass4 as A, Pass4 as B),--4,294,967,296 rows 
    Tally as (select row_number() over(order by C) as Number from Pass5) 
INSERT Example (ExampleId) 
select Number 
    from Tally 
    where Number <= @RowsToCreate 

L'indexation de la table après sa construction sera le meilleur moyen de l'indexer.

Oh, et j'appellerais cela une table "Tally". Je pense que c'est un terme commun, et vous pouvez trouver des tas de trucs et d'exemples en le googlant.

1

Certaines des méthodes proposées s'appuient sur des objets système (par exemple, sur les 'sys.objects'). Ils supposent que ces objets système contiennent suffisamment d'enregistrements pour générer nos numéros.

Je ne baserais sur rien qui n'appartient pas à mon application et sur lequel je n'ai pas le contrôle total. Par exemple: le contenu de ces tables sys peut changer, les tables peuvent ne plus être valides dans la nouvelle version de SQL, etc.

En tant que solution, nous pouvons créer notre propre table avec des enregistrements. Nous utilisons alors celui-ci à la place de ces objets liés au système (la table avec tous les nombres devrait être bien si nous connaissons la gamme à l'avance sinon nous pourrions aller pour celui à faire la jointure croisée dessus).

La solution basée sur CTE fonctionne correctement, mais elle a des limites liées aux boucles imbriquées.

2

Pour ceux qui cherchent une solution Azure

SET NOCOUNT ON  
CREATE TABLE Numbers (n bigint PRIMARY KEY)  
GO  
DECLARE @numbers table(number int); 
WITH numbers(number) as ( 
SELECT 1 AS number 
UNION all 
SELECT number+1 FROM numbers WHERE number<10000 
) 
INSERT INTO @numbers(number) 
SELECT number FROM numbers OPTION(maxrecursion 10000) 
INSERT INTO Numbers(n) SELECT number FROM @numbers 

Provenant du blog de l'équipe d'azur sql http://azure.microsoft.com/blog/2010/09/16/create-a-numbers-table-in-sql-azure/

12

Si vous faites tout cela dans SQL Server Management Studio ou sqlcmd, vous pouvez utiliser la fait que le séparateur de lots vous permet de répéter le lot:

CREATE TABLE Number (N INT IDENTITY(1,1) PRIMARY KEY NOT NULL); 
GO 

INSERT INTO Number DEFAULT VALUES; 
GO 100000 

Cela insérera 100000 enregistrements dans le Numbers tableau.

C'est lent. Il se compare à la méthode 1 dans la réponse de @ KM., Qui est le plus lent des exemples. Cependant, il s'agit de la lumière du code. Vous pouvez l'accélérer quelque peu en ajoutant la contrainte de clé primaire après le lot d'insertion.

+1

aucune idée que vous pourriez même répéter un lot comme ça! – Brad

+0

@Bacon Bits, puis-je insérer uniquement (a) une ou des colonnes spécifiques? – Azimuth

1

Voici quelques méthodes supplémentaires:
Méthode 1

IF OBJECT_ID('dbo.Numbers', 'U') IS NOT NULL 
    DROP TABLE dbo.Numbers 
GO 

CREATE TABLE Numbers (Number int NOT NULL PRIMARY KEY); 
GO 

DECLARE @i int = 1; 
INSERT INTO dbo.Numbers (Number) 
VALUES (1),(2); 

WHILE 2*@i < 1048576 
BEGIN 
    INSERT INTO dbo.Numbers (Number) 
    SELECT Number + 2*@i 
    FROM dbo.Numbers; 
    SET @i = @@ROWCOUNT; 
END 
GO 

SELECT COUNT(*) FROM Numbers AS RowCownt --1048576 rows 

Méthode 2

IF OBJECT_ID('dbo.Numbers', 'U') IS NOT NULL 
    DROP TABLE dbo.Numbers 
GO 

CREATE TABLE dbo.Numbers (Number int NOT NULL PRIMARY KEY); 
GO 

DECLARE @i INT = 0; 
INSERT INTO dbo.Numbers (Number) 
VALUES (1); 

WHILE @i <= 9 
BEGIN 
    INSERT INTO dbo.Numbers (Number) 
    SELECT N.Number + POWER(4, @i) * D.Digit 
    FROM dbo.Numbers AS N 
     CROSS JOIN (VALUES(1),(2),(3)) AS D(Digit) 
    ORDER BY D.Digit, N.Number 
    SET @i = @i + 1; 
END 
GO 

SELECT COUNT(*) FROM dbo.Numbers AS RowCownt --1048576 rows 

Méthode 3

IF OBJECT_ID('dbo.Numbers', 'U') IS NOT NULL 
    DROP TABLE dbo.Numbers 
GO 

CREATE TABLE Numbers (Number int identity NOT NULL PRIMARY KEY, T bit NULL); 

WITH 
    T1(T) AS (SELECT T FROM (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) AS T(T)) --10 rows 
    ,T2(T) AS (SELECT A.T FROM T1 AS A CROSS JOIN T1 AS B CROSS JOIN T1 AS C) --1,000 rows 
    ,T3(T) AS (SELECT A.T FROM T2 AS A CROSS JOIN T2 AS B CROSS JOIN T2 AS C) --1,000,000,000 rows 

INSERT INTO dbo.Numbers(T) 
SELECT TOP (1048576) NULL 
FROM T3; 

ALTER TABLE Numbers 
    DROP COLUMN T; 
GO 

SELECT COUNT(*) FROM dbo.Numbers AS RowCownt --1048576 rows 

Méthode 4, tirée de Defensive Database Programming livre par Alex Kuznetsov

IF OBJECT_ID('dbo.Numbers', 'U') IS NOT NULL 
    DROP TABLE dbo.Numbers 
GO 

CREATE TABLE Numbers (Number int NOT NULL PRIMARY KEY); 
GO 

DECLARE @i INT = 1 ; 
INSERT INTO dbo.Numbers (Number) 
VALUES (1); 

WHILE @i < 524289 --1048576 
BEGIN; 
    INSERT INTO dbo.Numbers (Number) 
    SELECT Number + @i 
    FROM dbo.Numbers; 
    SET @i = @i * 2 ; 
END 
GO 

SELECT COUNT(*) FROM dbo.Numbers AS RowCownt --1048576 rows 

Méthode 5, tirée de Arrays and Lists in SQL Server 2005 and Beyond article par Erland Sommarskog

IF OBJECT_ID('dbo.Numbers', 'U') IS NOT NULL 
    DROP TABLE dbo.Numbers 
GO 

CREATE TABLE Numbers (Number int NOT NULL PRIMARY KEY); 
GO 

WITH digits (d) AS (
    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 UNION ALL 
    SELECT 0) 
INSERT INTO Numbers (Number) 
    SELECT Number 
    FROM (SELECT i.d + ii.d * 10 + iii.d * 100 + iv.d * 1000 + 
        v.d * 10000 + vi.d * 100000 AS Number 
      FROM digits i 
      CROSS JOIN digits ii 
      CROSS JOIN digits iii 
      CROSS JOIN digits iv 
      CROSS JOIN digits v 
      CROSS JOIN digits vi) AS Numbers 
    WHERE Number > 0 
GO 

SELECT COUNT(*) FROM dbo.Numbers AS RowCownt --999999 rows 

Résumé:
Parmi ces 5 méthodes , la méthode 3 semble être la plus rapide.

1

Voici une courte et rapide solution en mémoire que je suis venu avec l'utilisation du Table Valued Constructors introduit dans SQL Server 2008:

--1,000,000 rows. Either add/remove CROSS JOINs, or use TOP clause to modify this 

;WITH v AS (SELECT * FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) v(z)) 

SELECT N FROM (SELECT ROW_NUMBER() OVER (ORDER BY v1.z)-1 N FROM v v1 
    CROSS JOIN v v2 CROSS JOIN v v3 CROSS JOIN v v4 CROSS JOIN v v5 CROSS JOIN v v6) Nums 

Notez que cela pourrait être calculé rapidement à la volée, ou (encore mieux) stockée dans une table permanente (ajouter une clause INTO après le segment SELECT N) avec une clé primaire sur le champ N pour une meilleure efficacité.

+0

J'aime cette idée si vous voulez un tableau de nombres à la volée. Il est plus lent que d'autres lorsque vous l'utilisez pour générer une table réelle. –

+0

@KM. Je viens de le tester sur ma configuration, il a fallu moins d'une seconde. Mais disons hypothétiquement qu'il a fallu 10 secondes, contre une autre qui n'a pris qu'une seconde (pour mettre en place la table permanente). IMO, c'est encore minuscule compte tenu, vous aurez seulement besoin de mettre en place la table permanente une fois. D'autres facteurs, comme la verbosité du code, seraient plus importants pour moi. 1 minute contre 1 seconde? ce serait un peu différent, mais ma requête n'est pas si lente. – iliketocode

1

Je sais que ce fil est vieux et répondu, mais il est un moyen de presser un peu la performance supplémentaire sur Méthode 7:

Au lieu de cela (essentiellement la méthode 7 mais avec une certaine facilité d'utilisation polonais):

DECLARE @BIT AS BIT = 0 
IF OBJECT_ID('tempdb..#TALLY') IS NOT NULL 
    DROP TABLE #TALLY 
DECLARE @RunDate datetime 
SET @RunDate=GETDATE() 
SELECT TOP 10000 IDENTITY(int,1,1) AS Number 
    INTO #TALLY 
    FROM sys.objects s1  --use sys.columns if you don't get enough rows returned to generate all the numbers you need 
    CROSS JOIN sys.objects s2 --use sys.co 
ALTER TABLE #TALLY ADD PRIMARY KEY(Number) 
PRINT CONVERT(varchar(20),datediff(ms,@RunDate,GETDATE()))+' milliseconds' 

Essayez ceci:

DECLARE @BIT AS BIT = 0 
IF OBJECT_ID('tempdb..#TALLY') IS NOT NULL 
    DROP TABLE #TALLY 
DECLARE @RunDate datetime 
SET @RunDate=GETDATE() 
SELECT TOP 10000 IDENTITY(int,1,1) AS Number 
    INTO #TALLY 
    FROM  (SELECT @BIT [X] UNION ALL SELECT @BIT) [T2] 
    CROSS JOIN (SELECT @BIT [X] UNION ALL SELECT @BIT) [T4] 
    CROSS JOIN (SELECT @BIT [X] UNION ALL SELECT @BIT) [T8] 
    CROSS JOIN (SELECT @BIT [X] UNION ALL SELECT @BIT) [T16] 
    CROSS JOIN (SELECT @BIT [X] UNION ALL SELECT @BIT) [T32] 
    CROSS JOIN (SELECT @BIT [X] UNION ALL SELECT @BIT) [T64] 
    CROSS JOIN (SELECT @BIT [X] UNION ALL SELECT @BIT) [T128] 
    CROSS JOIN (SELECT @BIT [X] UNION ALL SELECT @BIT) [T256] 
    CROSS JOIN (SELECT @BIT [X] UNION ALL SELECT @BIT) [T512] 
    CROSS JOIN (SELECT @BIT [X] UNION ALL SELECT @BIT) [T1024] 
    CROSS JOIN (SELECT @BIT [X] UNION ALL SELECT @BIT) [T2048] 
    CROSS JOIN (SELECT @BIT [X] UNION ALL SELECT @BIT) [T4096] 
    CROSS JOIN (SELECT @BIT [X] UNION ALL SELECT @BIT) [T8192] 
    CROSS JOIN (SELECT @BIT [X] UNION ALL SELECT @BIT) [T16384] 
ALTER TABLE #TALLY ADD PRIMARY KEY(Number) 
PRINT CONVERT(varchar(20),datediff(ms,@RunDate,GETDATE()))+' milliseconds' 

Sur mon serveur cela prend ~ 10 ms par opposition à la ~ 16-20 ms lors de la sélection de sys.objects. Cela a également l'avantage de ne pas dépendre du nombre d'objets dans sys.objects. Bien que ce soit assez sûr, c'est techniquement une dépendance et l'autre va plus vite quand même. Je pense que l'augmentation de la vitesse est en baisse à l'aide de BITS si vous changez:

DECLARE @BIT AS BIT = 0 

à:

DECLARE @BIT AS BIGINT = 0 

Il ajoute ~ 8-10 ms au temps total sur mon serveur. Cela dit, lorsque vous mettez à l'échelle jusqu'à 1 000 000 d'enregistrements, BIT vs BIGINT n'affecte plus sensiblement ma requête, mais il tourne toujours autour de ~ 680ms vs ~ 730ms de sys.objects.

Questions connexes