Pourquoi l'udf DelimitedSplit8k a-t-il été écrit avec 2X (produit cartésien) dans le server SQL?

Je posais cette question à propos de l'écriture rapide de la fonction de table en ligne dans le server sql.

Le code dans la réponse fonctionne mais je pose une question à propos de cette partie:

entrez la description de l'image ici

Il est clair pour moi qu'il voulait créer beaucoup de nombres (1,1,1,1,1, …) et ensuite les transformer en nombres séquentiels (1,2,3,4,5,6 …. ):

Dans cette partie :

WITH E1(N) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 ) ,E2(N) AS (SELECT 1 FROM E1 a, E1 b) ,E4(N) AS (SELECT 1 FROM E2 a, E2 b) SELECT * FROM e4 --10000 rows 

Il a créé 10000 lignes.

Cette fonction est largement utilisée et d'où ma question:

Question:

Pourquoi n'a-t-il pas (Jeff Moden) utilisé:

 WITH E1(N) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 ) ,E2(N) AS (SELECT 1 FROM E1 a, E1 b , E1 c , E1 d) SELECT * FROM E2 -- ALSO 10000 rows !!! 

Mais choisissez de le split en E2 , E4 ?

Bien que je ne sois pas Jeff Moden et que je ne sache pas son raisonnement, je trouve qu'il a probablement utilisé un model connu pour la génération de nombres qu'il appelle lui-même la méthode CTE d'Itzik Ben Gan dans cette réponse Stack Overflow .

Le motif va comme ceci:

 WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1), E02(N) AS (SELECT 1 FROM E00 a, E00 b), E04(N) AS (SELECT 1 FROM E02 a, E02 b), E08(N) AS (SELECT 1 FROM E04 a, E04 b), ... 

Afin d'adapter la méthode à sa fonction de fractionnement de strings, il a apparemment trouvé plus pratique de modifier le CTE initial à dix rangées au lieu de deux et de réduire le nombre de CTE à deux pour couvrir seulement les 8000 lignes nécessaires pour sa solution.

Heh … a juste traversé ça et a pensé que je répondrais.

Andriy M a répondu exactement à droite. Il était très modelé sur le très bon code original d'Itzik Ben-Gan BASE 2 et, oui, je l'ai changé (comme beaucoup d'autres) en code Base 10 juste pour réduire le nombre de cCTE (Cascading CTE). Le dernier code que moi et beaucoup d'autres utilisent réduit encore le nombre de cCTE. Il utilise également l'opérateur VALUES pour réduire la majeure partie du code, bien qu'il n'y ait aucun avantage de performance à le faire.

  WITH E1(N) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))E0(N)) --10 rows ,E4(N) AS (SELECT 1 FROM E1 a, E1 b, E1 c, E1 d) SELECT * FROM e4 --10000 rows ; 

Il y a beaucoup d'autres endroits où le besoin d'une telle création à la volée d'une séquence est nécessaire. Certains ont besoin de commencer la séquence à 0 et les autres à 1. Il y a aussi une plus grande gamme de valeurs nécessaires et, pour être honnête, je me suis fatigué d'écrire méticuleusement un code similaire à celui ci-dessus, donc j'ai fait ce que M. Ben-Gan et beaucoup d'autres ont fait. J'ai écrit un iTVF appelé "fnTally". Je n'utilise pas normalement la notation hongroise pour les fonctions mais j'avais deux raisons d'utiliser le préfixe "fn". 1) parce que je maintiens toujours une table de pointage physique et donc la fonction doit être nommée différemment et 2) Je peux dire aux gens au travail "Si vous aviez utilisé la fonction 'eff-n' Tally je vous ai parlé, vous ne le feriez pas J'ai ce problème "sans qu'il s'agisse réellement d'une violation des droits de l'homme. 😉

Juste au cas où quelqu'un aurait besoin d'une telle chose, voici le code que j'ai écrit pour ma version d'une fonction fnTally. Il y a un petit peu de compromis en lui permettant de commencer à 0 ou 1 performance sage, mais ça vaut la peine de la flexibilité supplémentaire, pour moi de toute façon. Et, oui … vous pourriez réduire le nombre de cCTE en faisant 12 CROSS JOINs dans la 2e et dernière cCTE. Je ne suis tout simplement pas allé dans cette direction. Tu pourrais sans mal.

Notez également que j'utilise toujours la méthode SELECT / UNION ALL pour former les 10 premières pseudo-lignes parce que je fais encore beaucoup de travail avec les gens en 2005 et que je suis resté coincé en 2005 jusqu'à il y a environ 6 mois. Une documentation complète est incluse dans le code.

  CREATE FUNCTION [dbo].[fnTally] /********************************************************************************************************************** Purpose: Return a column of BIGINTs from @ZeroOrOne up to and including @MaxN with a max value of 1 Trillion. As a performance note, it takes about 00:02:10 (hh:mm:ss) to generate 1 Billion numbers to a throw-away variable. Usage: --===== Syntax example (Returns BIGINT) SELECT tN FROM dbo.fnTally(@ZeroOrOne,@MaxN) t ; Notes: 1. Based on Itzik Ben-Gan's cascading CTE (cCTE) method for creating a "readless" Tally Table source of BIGINTs. Refer to the following URLs for how it works and introduction for how it replaces certain loops. http://www.sqlservercentral.com/articles/T-SQL/62867/ http://sqlmag.com/sql-server/virtual-auxiliary-table-numbers 2. To start a sequence at 0, @ZeroOrOne must be 0 or NULL. Any other value that's convertable to the BIT data-type will cause the sequence to start at 1. 3. If @ZeroOrOne = 1 and @MaxN = 0, no rows will be returned. 5. If @MaxN is negative or NULL, a "TOP" error will be returned. 6. @MaxN must be a positive number from >= the value of @ZeroOrOne up to and including 1 Billion. If a larger number is used, the function will silently truncate after 1 Billion. If you actually need a sequence with that many values, you should consider using a different tool. ;-) 7. There will be a substantial reduction in performance if "N" is sorted in descending order. If a descending sort is required, use code similar to the following. Performance will decrease by about 27% but it's still very fast especially compared with just doing a simple descending sort on "N", which is about 20 times slower. If @ZeroOrOne is a 0, in this case, remove the "+1" from the code. DECLARE @MaxN BIGINT; SELECT @MaxN = 1000; SELECT DescendingN = @MaxN-N+1 FROM dbo.fnTally(1,@MaxN); 8. There is no performance penalty for sorting "N" in ascending order because the output is explicity sorted by ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) Revision History: Rev 00 - Unknown - Jeff Moden - Initial creation with error handling for @MaxN. Rev 01 - 09 Feb 2013 - Jeff Moden - Modified to start at 0 or 1. Rev 02 - 16 May 2013 - Jeff Moden - Removed error handling for @MaxN because of exceptional cases. Rev 03 - 22 Apr 2015 - Jeff Moden - Modify to handle 1 Trillion rows for experimental purposes. **********************************************************************************************************************/ (@ZeroOrOne BIT, @MaxN BIGINT) RETURNS TABLE WITH SCHEMABINDING AS RETURN WITH E1(N) AS (SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1) --10E1 or 10 rows , E4(N) AS (SELECT 1 FROM E1 a, E1 b, E1 c, E1 d) --10E4 or 10 Thousand rows ,E12(N) AS (SELECT 1 FROM E4 a, E4 b, E4 c) --10E12 or 1 Trillion rows SELECT N = 0 WHERE ISNULL(@ZeroOrOne,0)= 0 --Conditionally start at 0. UNION ALL SELECT TOP(@MaxN) N = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E12 -- Values from 1 to @MaxN ;