Quels conseils généraux avez-vous pour jouer au golf dans T-SQL? Je cherche des idées qui peuvent être appliquées aux problèmes de golf de code en général qui sont au moins quelque peu spécifiques à T-SQL. Veuillez poster un pourboire par réponse.
Merci à Marcog pour l'idée originale. :)
Réponses:
Mon sac général de trucs ::
@
est une variable valide en t-sql.iif
une déclaration de cas de style VB. C'est presque toujours plus court qu'un équivalentif
else
.\
est un moyen utile pour initialiser un nombre à 0 dans un type monétaire. Vous pouvez convertir une valeur en un flottant en ajoutante
. par exemple4e
ou\k
qui mettra k à la valeur 0,00 argent.rCTE
semblent être le meilleur moyen de créer une table numérique de moins de 100 entrées. Encore plus court que d'utiliser spt_values. Si vous avez besoin de plus de 100, croisez-les et ajoutez-les.+=
et d'autres opérateurs composés ont été ajoutés en 2008. Utilisez-les pour enregistrer quelques caractères.;
.Select*from A,B where condition
est plus court queselect*from A join b on condition
goto
boucle de style do- while.STR()
est la fonction la plus courte pour transformer un int en chaîne. Si vous effectuez plusieurs conversions ou devez concaténer de nombreux types de données différents, envisagez laconcat
fonction. Par exemple,'hello'+str(@)
est plus court queconcat('hello',@)
, maishello+str(@)+str(@a)
est plus long queconcat('hello',@,@a)
Par exemple, ces deux sont sémantiquement équivalents.
Vous pouvez utiliser
Values
pour créer une table ou une sous-requête. Ce ne sera vraiment un avantage que si vous avez besoin de quelques lignes constantes.la source
Compression de code à l'aide de SQL
SQL est verbeux, obtient des scores élevés et autant que nous les aimons,
SELECT FROM WHERE
coûte 23 octets à chaque utilisation. Vous pouvez compresser ces mots et d'autres mots répétés ou des extraits de code entiers. Cela réduira le coût marginal du code répété à 1 octet! *Comment cela fonctionne:
Le problème:
Le coût initial est proche de 100 octets et chaque ligne de la table de remplacement coûte encore 6 octets. Ce type de logique ne sera pas très efficace à moins que vous ne travailliez avec beaucoup de code que vous ne pouvez pas réduire ou que le défi soit basé sur la compression.
Voici un exemple
Le défi consiste à obtenir les 10 derniers multiples de 2,3 et 5 menant à n. Disons que cela ( 343 octets golfés ) est la meilleure solution que j'ai pu trouver:
Exemple après compression du code
Cela exécute le même code que ci-dessus, soit environ 302 octets .
la source
SELECT @=REPLACE(@,i,j)FROM(VALUES(...)x(i,j)
au lieu d'utiliser une seule colonne avecLEFT()
etSUBSTRING()
. Si vous en avez 8 ou plus, éviter les guillemets et les virgules supplémentaires est un bon compromis.SET @=REPLACE(REPLACE(REPLACE(...
En voici une drôle. Cela convertira les valeurs d'une colonne en un seul tuple.
EDIT: Merci pour les commentaires. Il semble que le moyen le plus court de remonter sans les balises XML soit:
Remarque: si XML est une sortie valide, vous pouvez omettre la sélection externe et les parens. De plus
column1+''
, ne fonctionne que pour les chaînes. Pour les types de numéros, il est préférable de le fairecolumn1+0
la source
<column_name>value1</column_name><column_name>value2</column_name>...
. Afin d'avoir un CSV à partir d'une colonne, vous pouvezDECLARE @ VARCHAR(MAX)='';SELECT @+=column_name+',' FROM table_name;SELECT @
(merci pour le premier conseil de @ MichaelB) qui reviendravalue1,value2,...
. Cependant, il s'agit en fait de 9 caractères de plus que votre astuce XML :(Ltrim
n'est pas nécessaire car select (select ... for xml path ('')) renvoie unnvarchar(max)
. De plus, pour résoudre le problème des colonnes, utilisez simplement une expression non mutante. Pour les chiffres que vous pouvez fairev+0
, pour la chaîne, ajoutez une chaîne vide, etc. Bien que je ne considère pas vraiment cela comme une astuce de golf, c'est malheureusement une réalité de la façon d'écrire des requêtes dans le serveur SQL.Il est possible d'utiliser certains opérateurs au niveau du bit dans T-SQL .
Je n'ai pas d'exemple concret, mais je pense que c'est un fait à savoir lorsque l'on joue au golf en T-SQL.
la source
x=0 or y=0
, vous pouvez l'écrire comme l'équivalent logiquex|y=0
qui économise pas mal d'octets!Imprimer au lieu de sélectionner
C'est aussi simple que ça! Voici donc un polyglotte T-SQL / Python:
Essayez-le en ligne
la source
La notation scientifique est une méthode plus courte pour exprimer des nombres très grands et très petits, par exemple
select 1000000000
=select 1E9
etselect 0.000001
=select 1E-6
.la source
Michael B a mentionné l'utilisation d'un CTE récursif pour une table numérique , mais n'a pas montré d'exemple. Voici une version MS-SQL que nous avons élaborée dans cet autre thread :
Notez que vous pouvez modifier la valeur de départ (
1 n
), l' intervalle (n + 1
) et la valeur de fin (n < 99
).Si vous avez besoin de plus de 100 lignes, vous devrez cependant ajouter
option (maxrecursion 0)
:ou rejoindre le rCTE à lui-même:
Bien que ce dernier ne soit pas garanti de retourner dans l'ordre numérique sans
ORDER BY 1
la source
Utilisez la compression GZIP pour les chaînes très longues!
Je savais donc que SQL 2016 a ajouté une
COMPRESS
fonction (et unDECOMPRESS
fonction), ce qui (enfin) apporte la possibilité de GZIP une chaîne ou binaire.Le problème est qu'il n'est pas immédiatement clair comment en profiter pour jouer au golf;
COMPRESS
peut prendre une chaîne mais renvoie unVARBINARY
, qui est plus court en octets (lorsqu'il est stocké dans unVARBINARY
champ SQL ), mais plus long en caractères (hex brut).J'ai déjà joué avec ça auparavant, mais j'ai finalement pu mettre en place une version de travail, basée sur cette ancienne réponse sur SO . Ce message n'utilise pas les nouvelles fonctions GZIP, mais il convertit un
VARBINARY
en une chaîne encodée en Base-64. Nous avions juste besoin d'insérer les nouvelles fonctions au bon endroit et de les jouer un peu.Voici le code que vous pouvez utiliser pour convertir votre très longue chaîne en chaîne compressée encodée en Base-64:
Prenez la sortie et utilisez-la dans votre code à la place de la longue chaîne d'origine, avec:
Donc, au lieu de votre code d'origine ( 1471 octets )
vous auriez ceci ( 1034 octets ):
Voir cette réponse qui m'a fait économiser près de 200 octets.
Je n'ai pas fait le calcul, mais clairement en raison de la surcharge, cela ne sera efficace que pour les cordes extrêmement longues. Il y a probablement d'autres endroits où cela ne peut pas être utilisé; J'ai déjà découvert que vous devez le faire
SELECT
, vous ne pouvez pasPRINT
, sinon vous obtenez:EDIT : Version plus courte du code de décompression, gracieuseté de @digscoop :
Économisez 10 octets en changeant l'extérieur
CAST
en une conversion implicite en utilisantCONCAT
:Vous pouvez également déclarer une variable de type
XML
au lieu deVARCHAR(MAX)
et enregistrer sur l'intérieurCAST
:C'est un peu plus long en soi, mais si vous en avez besoin dans une variable pour d'autres raisons, cela pourrait aider.
la source
Quelques réflexions sur la création et l'utilisation de tableaux pour les défis:
1. L'entrée SQL peut être prise via une table préexistante
Code méthodes d'entrée / sortie de golf :
La création et le remplissage de ce tableau avec des valeurs d'entrée ne comptent pas dans votre total d'octets, vous pouvez simplement supposer qu'il est déjà là.
Cela signifie que vos calculs peuvent sortir via simple SELECT à partir du tableau d'entrée:
2. Si possible, ne créez pas du tout de table
Au lieu de (69 octets):
Faites juste (43 octets):
3. Si possible, créez la table avec SELECT INTO
Au lieu de (39 octets):
Faites ceci (17 octets):
4: Envisagez de mélanger plusieurs colonnes ensemble
Voici deux variantes qui renvoient la même sortie:
Après quelques tests, la version supérieure (plusieurs colonnes) semble plus courte avec 7 lignes ou moins , la version inférieure (en raison de LEFT et SUBSTRING) est plus courte avec 8 lignes ou plus . Votre kilométrage peut varier en fonction de vos données exactes.
5: Utilisez REPLACE et EXEC pour de très longues séquences de texte
Dans la veine de l'excellente réponse de comfortablydrei , si vous avez 15 valeurs ou plus , utilisez
REPLACE
un symbole pour vous débarrasser des répétitions'),('
séparateurs entre les éléments:114 caractères:
112 caractères:
Si vous utilisez déjà du SQL dynamique pour d'autres raisons (ou si vous avez plusieurs remplacements), le seuil où cela vaut la peine est beaucoup plus bas.
6: Utilisez un SELECT avec des colonnes nommées au lieu d'un tas de variables
Inspiré par l'excellente réponse de jmlt ici , réutilisez les chaînes via un SELECT:
Retour
(Pour MS SQL, j'ai changé le
\t
en un retour en ligne et changéCONCAT()
en+
pour économiser des octets).la source
Balisez votre code pour la coloration syntaxique T-SQL
Au lieu de simplement:
Inclure une balise de langue comme celle-ci:
et le résultat sera:
la source
Profitez des nouvelles fonctionnalités / fonctions de MS SQL 2016 et SQL 2017
Si vous n'avez pas de copies locales avec lesquelles travailler, vous pouvez jouer en ligne avec StackExchange Data Explorer (SQL 2016) ou avec dbfiddle.uk (SQL 2016 ou SQL "vNext").
STRING_SPLIT ( SQL 2016 et versions ultérieures )
Si vous devez alias la table ou faire référence au nom de la colonne:
TRIM ( SQL 2017 ou version ultérieure )
Plus court que
RTRIM()
et certainement plus court queLTRIM(RTRIM())
.A également une option pour supprimer d'autres caractères ou ensembles de caractères du début ou de la fin:
Retour
L Server 2
TRADUIRE ( SQL 2017 ou version ultérieure )
TRANSLATE
vous permet de remplacer plusieurs caractères en une seule étape, plutôt qu'un groupe d'REPLACE
instructions imbriquées . Mais ne célébrez pas trop , il ne remplace que des personnages individuels individuels par des personnages uniques différents.Chaque caractère de la deuxième chaîne est remplacé par le caractère correspondant de la troisième chaîne.
On dirait que nous pourrions éliminer un tas de personnages avec quelque chose comme
REPLACE(TRANSLATE('source string','ABCD','XXXX'),'X','')
Certains plus intéressants aussi, comme
CONCAT_WS
etSTRING_AGG
qui valent probablement le coup d'oeil aussi.la source
Sainte vache, j'ai découvert la merveille de
PARSENAME
( SQL 2012 ou supérieur ).La fonction a été conçue pour isoler les parties d'un nom d'objet comme
servername.dbname.dbo.tablename
, mais elle fonctionne pour toutes les valeurs séparées par des points. N'oubliez pas que cela compte à droite et non à gauche:Si vous avez moins de 4 valeurs séparées par des points, il reviendra
NULL
pour le reste (mais il compte toujours de droite à gauche ):C'est ici que la magie entre en jeu: combinez-la avec
STRING_SPLIT
(2016 ou plus) pour créer des tables multi-colonnes en mémoire !!Vieux et éclaté:
Nouvelle chaleur:
De toute évidence, vos économies réelles dépendent de la taille et du contenu de la table et de la façon dont vous l'utilisez.
Notez que si vos champs sont de largeur constante, vous feriez probablement mieux d'utiliser
LEFT
etRIGHT
de les séparer au lieu dePARSENAME
(non seulement parce que les noms de fonction sont plus courts, mais aussi parce que vous pouvez éliminer complètement les séparateurs).la source
Quelques astuces non liées que j'ai vues et que je voulais préserver:
GO #
pour répéter un bloc un certain nombre de fois .J'ai vu ce truc intelligent sur l'excellente réponse de Paul .
Bien entendu, cela réinitialisera toutes les variables de compteur dans le bloc, vous devrez donc les comparer à une
WHILE
boucle ou à unex: ... GOTO x
boucle.SELECT TOP ... FROM systypes
À partir de la même question que celle de Paul ci-dessus, Anuj Tripathi a utilisé l'astuce suivante :
ou, comme suggéré par pinkfloydx33 dans les commentaires:
Notez que cela ne repose sur aucun du contenu réel de
systypes
, juste que la vue système existe (ce qu'elle fait dans chaque base de données MS SQL) et contient au moins 10 lignes (elle semble en contenir 34, pour les versions les plus récentes de SQL ). Je n'ai pas trouvé de vues système avec des noms plus courts (qui ne nécessitaient pas desys.
préfixe), donc cela peut être idéal.la source
Voir cette question sur dba.stackexchange pour quelques idées intéressantes pour ajouter une colonne numérique à un résultat STRING_SPLIT.
Étant donné une chaîne comme
'one,two,three,four,five'
, nous voulons obtenir quelque chose comme:Selon la réponse de Joe Obbish, utilisez
ROW_NUMBER()
et triez parNULL
ou une constante:Selon la réponse de Paul White, utilisez
SEQUENCE
:Les séquences sont des objets persistants intéressants; vous pouvez définir le type de données, la valeur min et max, l'intervalle et si elle se termine au début:
Selon la réponse de Biju jose, vous pouvez utiliser la
IDENTITY()
fonction (qui n'est pas la même que laIDENTITY
propriété en conjonction avec un INSERT:Notez que les deux derniers paramètres de
IDENTITY(INT,1,1)
sont facultatifs et seront définis par défaut sur 1 s'ils sont exclus.la source
ORDER BY
si je peux m'en tirer (voir ma réponse à Toasty, Burnt, Brulee , par exemple).Je viens de découvrir que vous pouvez utiliser des chiffres pour un seul caractère
REPLACE
pour éliminer les guillemets :C'est parce que
REPLACE
fait une conversion implicite en chaîne.Les deux produisent la même sortie:
la source
_ et # sont des alias valides. Je les utilise avec CROSS APPLY pour faire apparaître les colonnes qu'il renvoie font partie de la clause FROM par exemple
J'aime ça quand le seul but du CROSS APPLY est de calculer une expression.
D'ailleurs, utiliser APPLY pour calculer des sous-expressions est un bon moyen de rendre votre code DRY-er (et plus court). D'après ce que j'ai vu dans les plans d'exécution, il n'y a aucun coût supplémentaire à cette approche. Le compilateur comprend que vous êtes en train de calculer quelque chose et le traite comme n'importe quelle autre expression.
la source