回答:
私の一般的なトリック::
@
t-sqlの有効な変数です。iif
はVBスタイルのcaseステートメントを追加しました。これは、ほとんどの場合、同等のものよりも短くなりif
else
ます。\
マネータイプで数値を0に初期化する便利な方法です。を追加すると、値をfloatに変換できe
ます。たとえば、4e
または\k
kを値0.00に設定します。rCTE
100エントリ未満の数値テーブルを作成する最良の方法のようです。spt_valuesを使用するよりもさらに短くなります。100個以上必要な場合は、クロスジョインして追加します。+=
その他の複合演算子は2008年に追加されました。それらを使用すると、いくつかの文字が節約されます。;
。Select*from A,B where condition
より短いselect*from A join b on condition
goto
ループとして書き直すことをお勧めします。STR()
intを文字列に変換する最短の関数です。複数の変換を行っている場合、または多数の異なるデータ型を連結する必要がある場合は、concat
関数を検討してください。たとえば'hello'+str(@)
、より短いがconcat('hello',@)
、hello+str(@)+str(@a)
より長いconcat('hello',@,@a)
たとえば、これら2つは意味的に同等です。
while @<100begin/*code*/set @+=1 end
s:/*code*/set @+=1if @<100goto s
を使用Values
して、テーブルまたはサブクエリを作成できます。これは、いくつかの定数行が必要な場合にのみ実際にメリットがあります。
SQLを使用したコード圧縮
SQLは冗長で、スコアが高く、私たちが愛する限り、SELECT FROM WHERE
使用するたびに23バイトかかります。これらおよび他の繰り返される単語またはコードスニペット全体を圧縮できます。これを行うと、繰り返されるコードの限界コストが1バイトに減少します!*
仕組み:
問題:
初期費用は100バイトに近く、置換テーブルの各行にはさらに6バイトかかります。この種類のロジックは、トリミングできない多くのコードで作業している場合や、チャレンジが圧縮ベースである場合を除き、あまり効果的ではありません。
ここに例があります
課題は、nまでの最後の10の2、3、および5の倍数を取得することです。これ(343 bytes golfed)が私が思いつく最高の解決策だとしましょう:
WITH x AS(
SELECT 99 n
UNION ALL
SELECT n-1
FROM x
WHERE n>1
)
SELECT w.n,t.n,f.n
FROM
(SELECT n, ROW_NUMBER()OVER(ORDER BY n DESC)r
FROM x WHERE n%2=0
)w
,
(SELECT n, ROW_NUMBER()OVER(ORDER BY n DESC)r
FROM x WHERE n%3=0
)t
, (SELECT n, ROW_NUMBER()OVER(ORDER BY n DESC)r
FROM x WHERE n%5=0
)f
WHERE w.r=t.r AND w.r=f.r AND w.r<11
ORDER BY 1
コードが圧縮された後の例
これは、上記と同じコードを実行し、最大302バイトのgolfedです。
DECLARE @a CHAR(999)='
WITH x AS(!99n UNION ALL !n-1 @x#n>1)
!w.n,t.n,f.n@$2=0)w,$3=0)t,$5=0)f
#w.r=t.r AND w.r=f.r AND w.r<11^1'
SELECT @a=REPLACE(@a,LEFT(i,1),SUBSTRING(i,2,99))
FROM(VALUES
('$(!n,ROW_NUMBER()OVER(^n DESC)r@x#n%'),
('! SELECT '),
('@ FROM '),
('# WHERE '),
('^ ORDER BY ')
)x(i)
EXEC(@a)
SELECT @=REPLACE(@,i,j)FROM(VALUES(...)x(i,j)
の代わりに単一の列を使用LEFT()
してSUBSTRING()
。8個以上ある場合、余分な引用符とコンマを避けることは良いトレードオフです。
SET @=REPLACE(REPLACE(REPLACE(...
ここに面白いものがあります。これにより、列の値が単一のタプルに変換されます。
編集:コメントありがとうございます。XMLタグなしでロールアップする最短の方法は次のようです。
SELECT (SELECT column1+''
FROM table
ORDER BY column1
FOR XML PATH(''))
注:XMLが有効な出力である場合、外側の選択と括弧を省略できます。また、column1+''
は文字列に対してのみ機能します。数値タイプの場合は、行うのが最善ですcolumn1+0
<column_name>value1</column_name><column_name>value2</column_name>...
ます。カラムからCSVをDECLARE @ VARCHAR(MAX)='';SELECT @+=column_name+',' FROM table_name;SELECT @
取得するには(@MichaelBの最初のヒントをありがとう)返されvalue1,value2,...
ます。ただし、実際にはXMLトリックよりも9文字長くなります。(
Ltrim
select(select ... for xml path( ''))はを返すため、必要ありませんnvarchar(max)
。また、列の問題を解決するには、非変化式を使用します。あなたができる数値の場合v+0
、文字列の場合は空の文字列を追加します。私はこれをゴルフのヒントとは本当に考えていませんが、これは悲しいことにSQLサーバーでクエリを書く方法の現実です。
Michael Bは、数値テーブルに再帰的CTEを使用することに言及しましたが、例を示しませんでした。この他のスレッドで作成したMS-SQLバージョンは次のとおりです。
--ungolfed
WITH t AS (
SELECT 1 n
UNION ALL
SELECT n + 1
FROM t
WHERE n < 99)
SELECT n FROM t
--golfed
WITH t AS(SELECT 1n UNION ALL SELECT n+1FROM t WHERE n<99)SELECT n FROM t
開始値(1 n
)、間隔(n + 1
)、終了値(n < 99
)を変更できることに注意してください。
ただし、100行を超える行が必要な場合は、追加する必要がありますoption (maxrecursion 0)
。
WITH t AS(SELECT 0n UNION ALL SELECT n+1FROM t WHERE n<9999)
SELECT n FROM t option(maxrecursion 0)
または、rCTE自体に参加します。
WITH t AS(SELECT 0n UNION ALL SELECT n+1FROM t WHERE n<99)
SELECT 100*z.n+t.n FROM t,t z
この最後のものは、 ORDER BY 1
そのため、SQL 2016がCOMPRESS
関数(およびDECOMPRESS
関数)を追加し、それが(最終的に)文字列またはバイナリをGZIPできるようにすることを知っていました。
問題は、ゴルフでこれをどのように利用するかがすぐにはわからないということです。COMPRESS
文字列をとることができますがVARBINARY
、バイト数は短い(SQL VARBINARY
フィールドに格納される場合)を返しますが、長い、文字数(生の16進数)です。
以前にこれで遊んだことがありますが、SOに関するこの古い回答に基づいて、ようやく作業バージョンをまとめることができました。その投稿は新しいGZIP関数を使用していませんが、VARBINARY
をBase-64エンコード文字列にます。新しい機能を適切な場所に挿入して、少しゴルフをする必要がありました。
非常に長い文字列をBase-64でエンコードされた圧縮文字列に変換するために使用できるコードは次のとおりです。
DECLARE @s VARCHAR(MAX)='Your really long string goes right here'
SELECT CONVERT(VARCHAR(MAX),(SELECT CONVERT(VARBINARY(MAX),COMPRESS(@s))
FOR XML PATH(''),BINARY BASE64))
出力を取得し、元の長い文字列の代わりにコード内で使用します。
--To use your compressed string and return the original:
DECLARE @e VARCHAR(MAX)='H4sIAAAAAAAEAIvMLy1SKEpNzMmpVMjJz0tXKC4pygRS6fmpxQpFmekZJQoZqUWpAGGwW5YnAAAA'
SELECT CAST(DECOMPRESS(CAST(@e as XML).value('.','varbinary(max)'))AS varchar(max))
したがって、元のコード(1471バイト)の代わりに
SELECT'Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal. Now we are engaged in a great civil war, testing whether that nation, or any nation so conceived and dedicated, can long endure. We are met on a great battle-field of that war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this. But, in a larger sense, we can not dedicate — we can not consecrate — we can not hallow — this ground. The brave men, living and dead, who struggled here, have consecrated it, far above our poor power to add or detract. The world will little note, nor long remember what we say here, but it can never forget what they did here. It is for us the living, rather, to be dedicated here to the unfinished work which they who fought here have thus far so nobly advanced. It is rather for us to be here dedicated to the great task remaining before us — that from these honored dead we take increased devotion to that cause for which they gave the last full measure of devotion — that we here highly resolve that these dead shall not have died in vain — that this nation, under God, shall have a new birth of freedom — and that government of the people, by the people, for the people, shall not perish from the earth.'
あなたはこれを持っているでしょう(1034バイト):
SELECT CAST(DECOMPRESS(CAST('H4sIAAAAAAAEAGVUW47bMAy8Cg/g5hD9aLFA0a8C/aYt2hZWEVNJjpGT5LodinE2i/0JIouPmeFQP3QrVCctQpwDVblKpptwqcSLkt3O3FbBeSy6LWujWUtbSTO1NVaaNLeYJbeBmLLslLlFzYNdTBKvEihm+hVHKe029CZBQpy44aYpighdil60RsvDmRtxSnQGEAasqUiPlX8bpxP91p126TeSF168PtNiYTTFa0y0cxmoSQWwhfZVDL8XPsBpAZLb40hVX9B+QgganCkp6kgOW5ET/fXmZ2mmwdF45NaSfJujpEA6ezfg6PErX8FDz2KEj9pIvUBJ63/E92xoBO3xP3Oi8iBxSTyJKY9ArQJSSiAltFhp8IuFEuBXL/TClc7RhmaXJ3prhJFxarq4KHNsvb6RtikcOkHhuuoGLkH7nE/0fcOIu9SJy4LAKrnKYKGmUdb2Qe3++hXSVpnKl+8rpoxh3t1HC9yVw4n+wA9jMVYwwGC4D3xBGOIY89rKtiwJwzINhkPfow0cAagzY8aj4sZMfFG1n90IKnEIZoEgrfDUvOmuBXT3COulaMM0kCieEdgNUOQsZ9gYEB4K8e0BYNwgbHNm2KBik4LCHgmhbxSigz1mYKPcane/Uxyo9D0bDN8oL0vS5/zYlC3DF7Gu+Ay872gQp9U7mDCzb2jPWN0ZaGJKwOJZx3QD9SvD6uEA4l2feHrvnv9lS93ojeu7ScHAAVFGme3tQOr94eGiZwuHSVeFduKDM70avwscZAtd++er+sqrp068VTf5C63D4HBdRfWtvwxcsYq2Ns8a96dvnTxMD7JYH0093+dQxcFU897DhLgO0V+RK0gdlbopj+cCzoRGPxX+89Se5u/dGPtzOIO5SAD5e3drL7LAfiXDyM13HE+d6CWZY26fjr7ZH+cPgFhJzPspK+FpbuvpP9RXxXK3BQAA'as XML).value('.','varbinary(max)'))AS varchar(max))
200バイト近く節約したこの回答をご覧ください。
まだ計算していませんが、明らかにオーバーヘッドのため、これは非常に長い文字列に対してのみ効果的です。使用できない場所はおそらく他にもあります。私はすでにあなたがSELECT
それを持っていることを発見しました、あなたはそれをすることができませんPRINT
、そうでなければあなたは得る:
Xml data type methods are not allowed in expressions in this context.
編集:@digscoopの好意による解凍コードの短いバージョン:
CAST
を使用して外部を暗黙的な変換に変更することにより、10バイトを節約しますCONCAT
。
SELECT CONCAT('',DECOMPRESS(CAST('encoded_string_here'as XML).value('.','varbinary(max)')))
のXML
代わりに型の変数を宣言VARCHAR(MAX)
し、内部で保存することもできますCAST
:
DECLARE @ XML='encoded_string_here'
SELECT CONCAT('',DECOMPRESS(@.value('.','varbinary(max)')))
これ自体はわずかに長くなりますが、他の理由で変数で必要な場合は役立つかもしれません。
チャレンジ用のテーブルの作成と使用に関するいくつかの考え:
1.既存のテーブルを介してSQL入力を取得できます。
SQLは名前付きテーブルから入力を取得できます
このテーブルを作成して入力値を入力しても、合計バイト数にはカウントされません。すでにテーブルにあると仮定できます。
これは、計算が入力テーブルからの単純なSELECTを介して出力できることを意味します。
SELECT 2*SQRT(a)FROM t
2.可能であれば、実際にテーブルを作成しないでください
代わりに(69バイト):
CREATE TABLE t(b INT)
INSERT t VALUES(7),(14),(21),(99)
SELECT b FROM t
ただ(43バイト):
SELECT b FROM(VALUES(7),(14),(21),(99))t(b)
3.可能であれば、SELECT INTOを使用してテーブルを作成します
代わりに(39バイト):
CREATE TABLE t(p INT)
INSERT t VALUES(2)
これを行う(17バイト):
SELECT 2 p INTO t
4:複数の列をまとめてマッシュすることを検討する
同じ出力を返す2つのバリエーションを次に示します。
SELECT a,b FROM
(VALUES('W','Bob'),('X','Sam'),('Y','Darla'),('Z','Elizabeth'))t(a,b)
SELECT LEFT(a,1),SUBSTRING(a,2,99)FROM
(VALUES('WBob'),('XSam'),('YDarla'),('ZElizabeth'))t(a)
いくつかのテストの後、一番上のバージョン(複数の列)は7行以下で短く見え、一番下のバージョン(LEFTとSUBSTRINGのため)は8行以上短くなります。正確なデータに応じて、走行距離は異なる場合があります。
5:非常に長いテキストシーケンスにはREPLACEおよびEXECを使用する
静脈ではcomfortablydreiの優秀な答えは、あなたが持っている場合は、15の以上の値を、使用REPLACE
シンボルには、繰り返しを取り除くため'),('
の要素間の区切り:
114文字:
SELECT a FROM(VALUES('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H')
,('I'),('J'),('K'),('L'),('M'),('N'),('O'))t(a)
112文字:
DECLARE @ CHAR(999)=REPLACE('SELECT a FROM(VALUES(''
A-B-C-D-E-F-G-H-I-J-K-L-M-N-O''))t(a)','-','''),(''')EXEC(@)
すでにしている場合他の理由(または複数の置き換えを持っている)ため、動的SQLを使用して、これは価値があるしきい値は、それははるかに低いです。
6:一連の変数の代わりに名前付き列でSELECTを使用する
ここでjmltの優れた答えに触発され、SELECTを介して文字列を再利用します。
SELECT a+b+a+b+d+b+b+a+a+d+a+c+a+c+d+c+c+a+a
FROM(SELECT'Hare 'a,'Krishna 'b,'Rama 'c,'
'd)t
戻り値
Hare Krishna Hare Krishna
Krishna Krishna Hare Hare
Hare Rama Hare Rama
Rama Rama Hare Hare
(MS SQLの場合\t
、これをインラインリターンに変更CONCAT()
し+
、バイトを保存するように変更しました)。
T-SQL構文の強調表示のためにコードにタグを付けます
ただの代わりに:
CREATE TABLE t(b INT)
INSERT t VALUES(7),(14),(21),(99)
SELECT b FROM t
次のような言語タグを含めます。
<!-- language: lang-sql -->
CREATE TABLE t(b INT)
INSERT t VALUES(7),(14),(21),(99)
SELECT b FROM t
結果は次のようになります。
CREATE TABLE t(b INT)
INSERT t VALUES(7),(14),(21),(99)
SELECT b FROM t
MS SQL 2016およびSQL 2017の新しい機能を利用する
使用するローカルコピーがない場合は、StackExchange Data Explorer (SQL 2016)またはdbfiddle.uk(SQL 2016またはSQL "vNext")でオンラインでプレイできます。
STRING_SPLIT(SQL 2016以降)
SELECT *
FROM STRING_SPLIT('one,two,three,four,five',',')
テーブルのエイリアスを作成するか、列名を参照する必要がある場合:
SELECT t.value
FROM STRING_SPLIT('one,two,three,four,five',',')t
TRIM(SQL 2017以降)
よりRTRIM()
短く、確かに短いLTRIM(RTRIM())
。
また、先頭または末尾から他の文字または文字セットを削除するオプションがあります。
SELECT TRIM('sq,0' FROM 'SQL Server 2000')
戻り値 L Server 2
TRANSLATE(SQL 2017以降)
TRANSLATE
一連のネストされたREPLACE
ステートメントではなく、1つのステップで複数の文字を置換できます。お祝いしません。しかし、あまりにも多くの、それだけで別の単一文字で個々の単一の文字を置き換えます。
SELECT TRANSLATE('2*[3+4]/{7-2}', '[]{}', '()()');
2番目の文字列の各文字は、3番目の文字列の対応する文字に置き換えられます。
次のようなものでたくさんのキャラクターを排除できるように見えます REPLACE(TRANSLATE('source string','ABCD','XXXX'),'X','')
いくつかのより興味深いものにも、同様CONCAT_WS
とSTRING_AGG
同様、おそらく一見の価値があります。
神聖な牛、私はPARSENAME
(SQL 2012以降)の不思議を発見しました。
この関数は、などのオブジェクト名の部分を分離するために構築されservername.dbname.dbo.tablename
ましたが、ドットで区切られた値に対して機能します。左ではなく、右から数えてください。
SELECT PARSENAME('a.b.c.d',1), -- d
PARSENAME('a.b.c.d',2), -- c
PARSENAME('a.b.c.d',3), -- b
PARSENAME('a.b.c.d',4) -- a
ドットで区切られた値が4つ未満の場合NULL
、残りは戻ります(ただし、右から左にカウントされます)。
SELECT PARSENAME('a.b',1), -- b
PARSENAME('a.b',2), -- a
PARSENAME('a.b',3), -- NULL
PARSENAME('a.b',4) -- NULL
ただし、魔法の出番です:STRING_SPLIT
(2016以降)と組み合わせて、メモリ内の複数列テーブルを作成します!!
古くて破壊された:
SELECT a,b,c FROM
(VALUES('Bob','W','Smith'),
('Sam','X','Johnson'),
('Darla','Y','Anderson'),
('Elizabeth','Z','Turner'))t(a,b,c)
新しい注目度:
SELECT PARSENAME(value,3)a,PARSENAME(value,2)b,PARSENAME(value,1)c
FROM string_split('Bob.W.Smith-Sam.X.Johnson-Darla.Y.Anderson-Elizabeth.Z.Turner','-')
明らかに、実際の節約は、テーブルのサイズと内容、および使用方法によって異なります。
フィールドが固定幅の場合は、代わりにを使用しLEFT
、RIGHT
代わりにそれらを分離する方がよいことに注意してくださいPARSENAME
(関数名が短いだけでなく、セパレータを完全に削除できるためです)。
私が見た、保存したかった、いくつかの無関係なトリック
GO #
特定の回数ブロックを繰り返すために使用します。ポールのすばらしい答えについて、この巧妙なトリックを見ました。
PRINT'**********'
GO 10
もちろん、これはブロック内のカウンター変数をリセットするため、WHILE
ループまたはループと比較する必要がありx: ... GOTO x
ます。
SELECT TOP ... FROM systypes
上記のポールと同じ質問から、アヌジトリパティは次のトリックを使用しました。
SELECT TOP 10 REPLICATE('*',10) FROM systypes
または、コメントでpinkfloydx33が示唆しているように:
SELECT TOP 10'**********'FROM systypes
これは実際のいずれにも依存しないことに注意してください コンテンツにsystypes
、システムビューが存在する(すべてのMS SQLデータベースに存在する)だけで、少なくとも10行を含む(SQLの最新バージョンでは34を含むように見える) )。短い名前(sys.
プレフィックスを必要としない)のシステムビューが見つからなかったため、これが理想的です。
dba.stackexchangeでこの質問を参照してくださいSTRING_SPLITの結果に数値列を追加するための興味深いアイデア。
のような文字列が与えられた場合、'one,two,three,four,five'
次のようなものを取得します。
value n
------ ---
one 1
two 2
three 3
four 4
five 5
ジョー・オブビッシュの回答、使用ROW_NUMBER()
および順序、NULL
または定数:
SELECT value, ROW_NUMBER() OVER(ORDER BY (SELECT 1))n
FROM STRING_SPLIT('one,two,three,four,five',',')
ポール・ホワイトの答えは1、使用SEQUENCE
:
CREATE SEQUENCE s START WITH 1
SELECT value, NEXT VALUE FOR s
FROM STRING_SPLIT('one,two,three,four,five', ',')
シーケンスは興味深い永続オブジェクトです。データ型、最小値と最大値、間隔、および先頭に折り返すかどうかを定義できます。
CREATE SEQUENCE s TINYINT; --Starts at 0
CREATE SEQUENCE s MINVALUE 1; --Shorter than START WITH
SELECT NEXT VALUE FOR s --Retrieves the next value from the sequence
ALTER SEQUENCE s RESTART; --Restarts a sequence to its original start value
Bijuホセの答えごとに、あなたは使用することができる機能である(ないと同じ性質 INSERTと組み合わせての:IDENTITY()
IDENTITY
SELECT value v,IDENTITY(INT) AS n
INTO t
FROM STRING_SPLIT('one,two,three,four,five',',')
SELECT * FROM t
の最後の2つのパラメーターIDENTITY(INT,1,1)
はオプションであり、除外するとデフォルトで1になることに注意してください。
ORDER BY
私がそれを逃れることができるなら、私が除外する方法に似ています(例えば、Toasty、Burnt、Bruleeへの私の答えを見てください)。
_および#は有効なエイリアスです。これらをCROSS APPLYで使用して、返される列がFROM句の一部であるように見せます。
SELECT TOP 10 number, n2
FROM master.dbo.spt_values v
CROSS APPLY (SELECT number*2 n2) _
CROSS APPLYの唯一の目的が式の計算である場合、私はこれが好きです。
さらに言えば、部分式の計算にAPPLYを使用することは、コードをDRY-er(およびそれより短く)にするための適切な方法です。私が実行計画で見たものから、このアプローチに追加コストはありません。コンパイラは、あなたが何かを計算しているだけであると判断し、他の式と同様に扱います。