Cette différence ne semble s'appliquer que lorsque l'objet est un arbre B +. Lors de la suppression primary key
de la variable sur la table, il s'agit donc d'un tas, j'ai obtenu les résultats suivants
2560
2120
2080
2130
2140
Mais avec le PK, j'ai également trouvé un schéma similaire dans mes tests avec des résultats typiques ci-dessous.
+--------+--------+---------+-------------------+
| @table | #table | ##table | [permanent_table] |
+--------+--------+---------+-------------------+
| 2670 | 2683 | 9603 | 9703 |
| 6823 | 6840 | 9723 | 9790 |
| 6813 | 6816 | 9626 | 9703 |
| 6883 | 6816 | 9600 | 9716 |
| 6840 | 6856 | 9610 | 9673 |
+--------+--------+---------+-------------------+
Ma théorie est qu'il existe une certaine optimisation disponible lors de l'insertion en bloc dans les arborescences B + temporaires locales qui ne s'applique que si aucune page n'est déjà allouée.
Je fonde cela sur les observations suivantes.
Lors de l'exécution de différentes versions de votre code de test, je n'ai vu ce modèle qu'avec les tables @table_variables
et #temp
. Pas de tables permanentes tempdb
ni de ##
tables.
Pour obtenir des performances plus lentes, il n'est pas nécessaire d'avoir précédemment ajouté et supprimé une grande quantité de lignes du tableau. Il suffit d'ajouter une seule ligne et de la laisser dedans.
TRUNCATE
désalloue toutes les pages du tableau. DELETE
n'entraîne pas la désallocation de la dernière page du tableau.
L'utilisation du profileur VS 2012 montre que dans le cas le plus rapide, SQL Server utilise un chemin de code différent. 36% du temps est consacré à sqlmin.dll!RowsetBulk::InsertRow
61% du temps consacré au sqlmin.dll!RowsetNewSS::InsertRow
cas le plus lent.
Fonctionnement
SELECT *
FROM sys.dm_db_index_physical_stats(2,OBJECT_ID('tempdb..#values'),1,NULL, 'DETAILED')
après le retour de la suppression
+-------------+------------+--------------+--------------------+
| index_level | page_count | record_count | ghost_record_count |
+-------------+------------+--------------+--------------------+
| 0 | 1 | 0 | 1 |
| 1 | 1 | 1 | 0 |
| 2 | 1 | 1 | 0 |
+-------------+------------+--------------+--------------------+
J'ai trouvé qu'il était possible de réduire quelque peu l'écart temporel en activant l'indicateur de trace 610 .
Cela a eu pour effet de réduire la quantité de l' exploitation forestière essentiellement pour les insertions suivantes (vers le bas de 350 Mo à 103 Mo , car il ne consigne plus les valeurs des lignes individuelles insérées) , mais ce qui a eu qu'une amélioration mineure dans timings pour les 2e et suivants @table
, les #table
cas et l'écart demeure. L'indicateur de trace a considérablement amélioré les performances générales des insertions vers les deux autres types de table.
+--------+--------+---------+-------------------+
| @table | #table | ##table | [permanent_table] |
+--------+--------+---------+-------------------+
| 2663 | 2670 | 5403 | 5426 |
| 5390 | 5396 | 5410 | 5403 |
| 5373 | 5390 | 5410 | 5403 |
| 5393 | 5410 | 5406 | 5433 |
| 5386 | 5396 | 5390 | 5420 |
+--------+--------+---------+-------------------+
En regardant dans le journal des transactions, j'ai remarqué que les insertions initiales contre les tables temporaires locales vides semblent encore plus minimalement enregistrées (à 96 Mo).
Notamment, ces insertions plus rapides n'avaient que des 657
transactions ( LOP_BEGIN_XACT
/ LOP_COMMIT_XACT
paires) par rapport à plus 10,000
dans les cas plus lents. En particulier, les LOP_FORMAT_PAGE
opérations semblent beaucoup réduites. Les cas plus lents ont une entrée de journal des transactions pour cela pour chaque page du tableau (environ 10,270
) par rapport à seulement 4
ces entrées dans le cas rapide.
Le journal utilisé dans les trois cas était le suivant (j'ai supprimé les enregistrements du journal pour les mises à jour des tables de base du système afin de réduire la quantité de texte mais ils sont toujours inclus dans les totaux)
Enregistrement de la première insertion contre @table_var
(96,5 Mo)
+-----------------------+----------+----------------------------------------------+---------------+---------+
| Operation | Context | AllocUnitName | Size in Bytes | Cnt |
+-----------------------+----------+----------------------------------------------+---------------+---------+
| LOP_BEGIN_XACT | LCX_NULL | NULL | 83876 | 658 |
| LOP_COMMIT_XACT | LCX_NULL | NULL | 34164 | 657 |
| LOP_CREATE_ALLOCCHAIN | LCX_NULL | NULL | 120 | 3 |
| LOP_FORMAT_PAGE | LCX_HEAP | dbo.#531856C7 | 84 | 1 |
| LOP_FORMAT_PAGE | LCX_IAM | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 84 | 1 |
| LOP_FORMAT_PAGE | LCX_IAM | dbo.#531856C7 | 84 | 1 |
| LOP_FORMAT_PAGE | LCX_IAM | Unknown Alloc Unit | 84 | 1 |
| LOP_HOBT_DDL | LCX_NULL | NULL | 216 | 6 |
| LOP_HOBT_DELTA | LCX_NULL | NULL | 320 | 5 |
| LOP_IDENT_NEWVAL | LCX_NULL | NULL | 100240000 | 2506000 |
| LOP_INSERT_ROWS | LCX_HEAP | dbo.#531856C7 | 72 | 1 |
| LOP_MODIFY_ROW | LCX_IAM | dbo.#531856C7 | 88 | 1 |
| LOP_MODIFY_ROW | LCX_PFS | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 158592 | 1848 |
| LOP_MODIFY_ROW | LCX_PFS | dbo.#531856C7 | 80 | 1 |
| LOP_MODIFY_ROW | LCX_PFS | Unknown Alloc Unit | 216016 | 2455 |
| LOP_SET_BITS | LCX_GAM | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 84360 | 1406 |
| LOP_SET_BITS | LCX_GAM | Unknown Alloc Unit | 147120 | 2452 |
| LOP_SET_BITS | LCX_IAM | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 84360 | 1406 |
| LOP_SET_BITS | LCX_IAM | Unknown Alloc Unit | 147120 | 2452 |
| Total | NULL | NULL | 101209792 | 2519475 |
+-----------------------+----------+----------------------------------------------+---------------+---------+
Déconnexion des insertions suivantes TF 610 (350 Mo)
+-----------------------+--------------------+----------------------------------------------+---------------+---------+
| Operation | Context | AllocUnitName | Size in Bytes | Cnt |
+-----------------------+--------------------+----------------------------------------------+---------------+---------+
| LOP_BEGIN_CKPT | LCX_NULL | NULL | 96 | 1 |
| LOP_BEGIN_XACT | LCX_NULL | NULL | 1520696 | 12521 |
| LOP_COMMIT_XACT | LCX_NULL | NULL | 651040 | 12520 |
| LOP_CREATE_ALLOCCHAIN | LCX_NULL | NULL | 40 | 1 |
| LOP_DELETE_SPLIT | LCX_INDEX_INTERIOR | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 2160 | 36 |
| LOP_END_CKPT | LCX_NULL | NULL | 136 | 1 |
| LOP_FORMAT_PAGE | LCX_HEAP | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 859236 | 10229 |
| LOP_FORMAT_PAGE | LCX_IAM | Unknown Alloc Unit | 84 | 1 |
| LOP_FORMAT_PAGE | LCX_INDEX_INTERIOR | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 3108 | 37 |
| LOP_HOBT_DDL | LCX_NULL | NULL | 648 | 18 |
| LOP_HOBT_DELTA | LCX_NULL | NULL | 657088 | 10267 |
| LOP_IDENT_NEWVAL | LCX_NULL | NULL | 100239960 | 2505999 |
| LOP_INSERT_ROWS | LCX_CLUSTERED | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 258628000 | 2506000 |
| LOP_INSERT_ROWS | LCX_HEAP | dbo.#531856C7 | 72 | 1 |
| LOP_INSERT_ROWS | LCX_INDEX_INTERIOR | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 1042776 | 10302 |
| LOP_MODIFY_HEADER | LCX_HEAP | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 859236 | 10229 |
| LOP_MODIFY_HEADER | LCX_INDEX_INTERIOR | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 3192 | 38 |
| LOP_MODIFY_ROW | LCX_IAM | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 704 | 8 |
| LOP_MODIFY_ROW | LCX_PFS | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 934264 | 11550 |
| LOP_MODIFY_ROW | LCX_PFS | Unknown Alloc Unit | 783984 | 8909 |
| LOP_SET_BITS | LCX_GAM | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 76980 | 1283 |
| LOP_SET_BITS | LCX_GAM | Unknown Alloc Unit | 534480 | 8908 |
| LOP_SET_BITS | LCX_IAM | dbo.#4F47C5E3.PK__#4F47C5E__3213E83F51300E55 | 76980 | 1283 |
| LOP_SET_BITS | LCX_IAM | Unknown Alloc Unit | 534480 | 8908 |
| LOP_SHRINK_NOOP | LCX_NULL | NULL | 32 | 1 |
| LOP_XACT_CKPT | LCX_NULL | NULL | 92 | 1 |
| Total | NULL | NULL | 367438748 | 5119297 |
+-----------------------+--------------------+----------------------------------------------+---------------+---------+
Enregistrement des insertions suivantes TF 610 (103 Mo)
+-------------------------+-------------------------+----------------------------------------------+---------------+---------+
| Operation | Context | AllocUnitName | Size in Bytes | Cnt |
+-------------------------+-------------------------+----------------------------------------------+---------------+---------+
| LOP_BEGIN_CKPT | LCX_NULL | NULL | 192 | 2 |
| LOP_BEGIN_XACT | LCX_NULL | NULL | 1339796 | 11099 |
| LOP_BULK_EXT_ALLOCATION | LCX_NULL | NULL | 20616 | 162 |
| LOP_COMMIT_XACT | LCX_NULL | NULL | 577096 | 11098 |
| LOP_CREATE_ALLOCCHAIN | LCX_NULL | NULL | 40 | 1 |
| LOP_DELETE_SPLIT | LCX_INDEX_INTERIOR | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 | 2160 | 36 |
| LOP_END_CKPT | LCX_NULL | NULL | 272 | 2 |
| LOP_FORMAT_PAGE | LCX_BULK_OPERATION_PAGE | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 | 863520 | 10280 |
| LOP_FORMAT_PAGE | LCX_IAM | Unknown Alloc Unit | 84 | 1 |
| LOP_FORMAT_PAGE | LCX_INDEX_INTERIOR | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 | 3108 | 37 |
| LOP_HOBT_DELTA | LCX_NULL | NULL | 666496 | 10414 |
| LOP_IDENT_NEWVAL | LCX_NULL | NULL | 100239960 | 2505999 |
| LOP_INSERT_ROWS | LCX_CLUSTERED | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 | 23544 | 218 |
| LOP_INSERT_ROWS | LCX_HEAP | dbo.#719CDDE7 | 72 | 1 |
| LOP_INSERT_ROWS | LCX_INDEX_INTERIOR | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 | 1042776 | 10302 |
| LOP_MODIFY_HEADER | LCX_BULK_OPERATION_PAGE | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 | 780216 | 10266 |
| LOP_MODIFY_HEADER | LCX_HEAP | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 | 1718472 | 20458 |
| LOP_MODIFY_HEADER | LCX_INDEX_INTERIOR | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 | 3192 | 38 |
| LOP_MODIFY_ROW | LCX_IAM | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 | 704 | 8 |
| LOP_MODIFY_ROW | LCX_PFS | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 | 114832 | 1307 |
| LOP_MODIFY_ROW | LCX_PFS | Unknown Alloc Unit | 231696 | 2633 |
| LOP_RANGE_INSERT | LCX_NULL | NULL | 48 | 1 |
| LOP_SET_BITS | LCX_GAM | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 | 77100 | 1285 |
| LOP_SET_BITS | LCX_GAM | Unknown Alloc Unit | 157920 | 2632 |
| LOP_SET_BITS | LCX_IAM | dbo.#6DCC4D03.PK__#6DCC4D0__3213E83F6FB49575 | 77100 | 1285 |
| LOP_SET_BITS | LCX_IAM | Unknown Alloc Unit | 157920 | 2632 |
| LOP_XACT_CKPT | LCX_NULL | NULL | 92 | 1 |
| Total | NULL | NULL | 108102960 | 2602218 |
+-------------------------+-------------------------+----------------------------------------------+---------------+---------+
TRUNCATE
plusDELETE
à lui seul plaiderait également pour cela. Je considérerais également rarement les variables de table pour un grand nombre de lignes.10,000+
pages qui se fait de manière beaucoup plus optimisée et semble en éviter une surcharge par page. Pour les inserts plus petits, je m'attendrais à ce qu'une telle différence soit moins significative.Observation et spéculation. . .
Sur certains systèmes, CURRENT_TIMESTAMP est défini comme l'heure au début de la transaction en cours. Une recherche rapide n'a révélé aucune documentation définitive sur le comportement de CURRENT_TIMESTAMP sur SQL Server. Mais le mode par défaut de SQL Server est de valider automatiquement les transactions, et il n'y a pas de BEGIN TRANSACTION ici, donc cela devrait être l'heure immédiatement avant l'instruction INSERT. (L'instruction DELETE doit automatiquement être validée et, quelle que soit la façon dont CURRENT_TIMESTAMP fonctionne sur SQL Server, elle ne doit avoir rien à voir avec l'instruction DELETE lorsque vous utilisez des transactions automatiquement validées.)
Lors de la première itération, l'instruction DELETE n'a pas vraiment de travail à faire et il n'y a aucune ligne individuelle à consigner. L'optimiseur le sait peut-être, ce qui réduit le temps de la première itération. (La combinaison d'aucune ligne à supprimer et d'aucune ligne individuelle à consigner.)
Vous pouvez tester cela (je pense) en l'insérant avant de le supprimer.
la source