ブロッコリーを克服するまでプログラミング

プログラミングとか趣味とかを報告します。

SQLServer : XMLの値と属性を「replace value of」で改変する

いままでSQLServerXML操作については、SELECT + FOR XML PATHでXMLを作成したり、OPENXMLでSELECTしたりしかしてこなかった僕です。

 

まぁ必ずではなかったのですが、ストアド上でXMLの値とか属性とかを個別に操作したいという案件が発生。「SQLServer XML 操作」とか調べてみると、上述している案件ばかりでてくるので、DOM操作でやろうかなとあきらめてたところ、それっぽいドキュメントを発見。

 



docs.microsoft.com

 

何はともあれ、Management Studioで以下のSQLを試してみた。

XML型の@xmlを宣言して、2番目の<TEST>タグの値と属性を5に変更しました。

 

DECLARE @xml XML = '<ROOT><TEST attr="10">1</TEST><TEST attr="20">2</TEST></ROOT>'

SELECT @xml

SET @xml.modify('replace value of (/ROOT/TEST[2]/text())[1] with "5"')

SELECT @xml

SET @xml.modify('replace value of (/ROOT/TEST[2]/@attr)[1] with "5"')

SELECT @xml

 

結果が下図。値も属性も改変できることを確認できた。めでたしめでたし。

f:id:atwtama:20190227234622p:plain

 

SQLServer : FOR XML PATHについてあまり考えてこなかったかも

いままであまり深く考えていなかったけれども、SQLServerのFOR XML PATH句について、「タグは存在するけど、数値(文字列)がない」もしくは「タグ自体が存在しない」の2パターンについて考えてみた。

 

正直、いままでこれらの違いについて考えてたことなかった。しかしある時、いつも使用していたNULL判定について、挙動がおかしいことに気づいた。なので、それぞれについて以下のクエリで試してみた。

 

  -- テスト用XMLを定義。ARG1, ARG2タグには値を挿入し、ARG3は値無し、ARG4はタグ自体無しとした。

  DECLARE @xml XML =

  '<ROOT>

 <ARG1>1</ARG1>

 <ARG2>2</ARG2>

 <ARG3></ARG3>

  </ROOT>'

 

  DECLARE @xml_predoc INT
  EXEC sp_xml_preparedocument @xml_predoc OUTPUT, @xml

 

  -- ARG1, 2, 3, 4について同条件でSELECTする。

  SELECT
  ARG1 ,ARG2 ,ARG3, ARG4
  FROM OPENXML(@xml_predoc, '/ROOT', 2)
  WITH( ARG1 INT, ARG2 INT, ARG3 INT, ARG4 INT)
  FOR XML PATH('ROOT')

 

このクエリを実行すると、結果は以下となる。

<ROOT>

 <ARG1>1</ARG1>

 <ARG2>2</ARG2>

 <ARG3>0</ARG3>

</ROOT>

 

 つまり、

1, タグはあるけど値を記載していない = 整数型なら0 。文字列型なら空文字。

2, タグ自体ないものに対してSELECTする = NULL

 

本当にあまり考えてこなかったけれども、パターン1については0になると考えると、条件文など適当に書くとエラーが出そう。

 

もしかしたら基礎なのかもしれないけれども、備忘録として。

 

 

SQLServer : エラーが出ていないのにストアドプロシージャの処理が止まっている?

スレッド内でSQLServerのストアドプロシージャを SQLExecDirect / SQLExecute で実行するサービスプログラムを作成した。だがしかし、なぜかストアドプロシージャの実行がうまくいっていないようであった。

ストアドプロシージャはデータの集計 + INSERTしか行っておらず、SQLServer Management Studio上で当該のストアドプロシージャを実行しても、エラーが一切確認できなかった。

 

ログやイベントビューアーなどから始まり、怪しい行を消したりCURSORをいじったり、SQLSetStmtAttrをいじったり、早3時間・・・。いろいろ行って分かったのは、「CURSORループ内でINSERTを使用すると止まる」ということであった。

 

INSERT特有といえば、Management Studio上でINSERT構文を実行すると以下のメッセージが現れることくらいである。

 

f:id:atwtama:20180418214134p:plain

 

これやろ!!!!!なんとなくやけどこの(1行処理されました)という処理行数メッセージ!!!Management Studio上では自然におるけど、絶対に悪い奴やろ!!!とりあえず、ストアドプロシージャ内に「SET NO COUNT ON」を記述すれば消えます。この1行を追加するだけで、(1行処理されました)と出力されていた状態から、以下のように「コマンドは正常に完了しました。」という文字列に変わりました。

 

f:id:atwtama:20180418214407p:plain

 

まじで、これだけで、正常にサービスからストアドプロシージャが実行されるようになりました。めでたしめでたし。

 

 

 

その後、突然のデジャブから、一年前に作成した同様の処理を行うストアドプロシージャを確認すると、SET NO COUNT ONが記述されていた。つまり、私は当該問題に一度ぶちあたり、解決していたのにもかかわらず、忘れていたのである。

 

なので、備忘録としてここに記す。

VBScript : Response.Formで受け取ると、すでにURLデコードされている?

一般的なのかわからないのですが、URLエンコードされた文字列をVBScript の Response.Formで 受け取ったら、受け取った段階でURLデコードされているという話。

 

類まれなる例なのかもしれませんが、こういった場合がありました。

クライアント側からURLエンコードしたデータをPOSTで送るから、VBScriptで受けてという話です。

で、私はサーバー側担当なので、「あ、URLデコード書かないといけないんだな」と思ったのですが、いざ取得データを確認してみるとすでにURLデコードされている状態でした。

 

ざっくり検索してもわからなかったし、これ以上踏み込む気もなかったので、とりあえず実証して終わりました。(クソプログラマー筆頭)

 

まず、VBScriptで以下のようなPOSTを受け取りResponseするだけの簡易的なプログラムを書きます。

 

test.asp

<%@ LANGUAGE = "VBScript" codepage="65001" %>

<%
Dim test01
Dim test02

test01 = Request.Form("test01")
test02 = Request.Form("test02")

Response.write "<TEST01>"
Response.write test01
Response.write "</TEST01><TEST02>"
Response.write test02
Response.write "</TEST02>"
%>

 

次に、以下のような2つの文章を用意します。

test01

これはテスト文章です。
2行目です。

 

test02

二つ目の文章です。

 

その後、NKFで上記文章をURLエンコードし、POST準備したものが以下のようなものです。

 

POST準備

test01=%E3%81%93%E3%82%8C%E3%81%AF%E3%83%86%E3%82%B9%E3%83%88%E6 %96%87%E7%AB%A0%E3%81%A7%E3%81%99%E3%80%82%0D%0A2%E8%A1%8C%E7%9B%AE %E3%81%A7%E3%81%99%E3%80%82&
test02=%E4%BA%8C%E3%81%A4%E7%9B%AE%E3%81%AE%E6%96%87%E7%AB%A0 %E3%81%A7%E3%81%99%E3%80%82

 

これをVBScriptにPOSTし、Responseを確認すると、以下のようになっています。

 

受信内容

<TEST01>これはテスト文章です。
2行目です。</TEST01><TEST02>二つ目の文章です。</TEST02>

 

以上からわかることは、VBScriptってすげぇな。

SQLServer : INSERT について、 SELECT UNION ALL と ( ), ( ),

先日、 INSERT ( ), ( ), ( ) がどうこうとか書いてるんですよね

litz.hatenablog.com

 

で、INSERT SELECT UNION ALL という書き方もあるんですよね。

例えば、以下のような感じ。

*********************************************************************************

INSERT INTO TESTDB

SELECT 1, 1, 1, 1, 1

UNION ALL

SELECT 2, 1, 1, 1, 1

UNION ALL

SELECT 3, 1, 1, 1, 1

*********************************************************************************

 

INSERT ( ), ( ), ( ) とプランを比較しました。

 

f:id:atwtama:20180309232219p:plain

 

同じですよね~ですよね~