SQL serverで擬似乱数生成を含むビューとプロシージャを作る
せっかくのクリスマスイヴなので、乱数生成を含むビュー&ストアドを作ってみる。
いや、クリスマスイヴは関係ない。申し訳ない。
1からnまでの乱数を生成する
大変ありがたいことに、T-SQLには擬似乱数を返すRAND()が存在する。
返り値は浮動小数点数(float)だそうだ。……浮動小数点数ってなんだ?
ぐぐった。
- decimal(p,s)
- 固定小数点型の数値。numericも同じ。
- 桁数指定ができる。pは値全体の桁数。整数部(p-s)桁、小数点以下s桁となる。
- 小数点以下は指定した桁数で丸められる。
- 整数部は指定した桁数を超えた場合は算術エラーになる。
- float
- 浮動小数点型の数値。
- 整数部と小数部を合わせて、realは4桁、floatは8桁まで。値によって小数点の位置を変える。
- 桁数制限に引っかかり次第、小数点以下を丸める。
固定小数点型の精度は一定だが容量を食う、
浮動小数点型の精度は不定だが容量をとらない、といった感じだろうか。
普段はデータ削減の要がなければ固定小数点型を使った方がよさそう。
でもRAND()が返す値は基本的に整数部が一桁(0.~ か 1.000 のどちらか)。
よって、今回はあまり関係なさそう。
とりあえずSQLでクエリを走らせてみる。
SELECT RAND(), RAND()
元気に乱数っぽいものを出してくれる。
「擬似」乱数とわざわざ名乗るからには色々込み入った問題があるんだろうなー。
そこには深入りしない。
これを材料に、1からnの整数を作る。
select -- 1から2のランダムな整数を出力 CONVERT(int,RAND() * 2)+1 -- 1から12のランダムな整数を出力 ,CONVERT(int,RAND() * 12)+1
できた。
次はこれをストアドにする。
1からnまでの乱数を生成するはずだった
今度はこのへんを参考にした。
途中までMySQLの記事を読んでいた。つらい。
とりあえず書いてみる。
返り値が全然わかってない。
USE AW4me go ALTER PROCEDURE [dbo].[KBS_FUNC]( --プロシージャ名のかっこの中に引数や返り値を指定する --引き数はこんな感じ 今回はデフォルトで3を入れる @KBS_i_1 int = 3 --返り値はこんな感じ ,@KBS_o_1 varchar(max) = '' OUTPUT ) --引き数と返り値の設定が終わったらAS AS --BEGIN~ENDでブロックを作る BEGIN --変数の宣言 --繰り返しの変数を入れる DECLARE @i int; --乱数のための変数も入れる DECLARE @r int; --結果出力のための文字列入れを作る DECLARE @vartmp varchar(256); DECLARE @varOUT varchar(256) = ''; --繰り返しの数を1に設定 SELECT @i = 0; --繰り返し処理を開始 WHILE @i < @KBS_i_1 BEGIN --1から3の乱数を作ってrに入れる SET @r = CONVERT(int,RAND() * 3)+1; --@rが1なら金 IF @r = 1 BEGIN SET @vartmp = '金'; --次の単語があるようならカンマを入れ、ないようなら!を入れる IF @i = @KBS_i_1 - 1 --次の単語なしなら'!' SET @vartmp = @vartmp + '!'; ELSE SET @vartmp = @vartmp + '、'; END ELSE BEGIN --@rが2なら暴力 IF @r = 2 BEGIN SET @vartmp = '暴力'; --次の単語があるようならカンマを入れ、ないようなら!を入れる IF @i = @KBS_i_1 - 1 --次の単語なしなら'!' SET @vartmp = @vartmp + '!'; ELSE SET @vartmp = @vartmp + '、'; END ELSE BEGIN --@rが3ならSEX IF @r = 3 BEGIN SET @vartmp = 'SEX'; --次の単語があるようならカンマを入れ、ないようなら!を入れる IF @i = @KBS_i_1 - 1 --次の単語なしなら'!' SET @vartmp = @vartmp + '!'; ELSE SET @vartmp = @vartmp + '、'; END ELSE SET @vartmp = @vartmp + ''; END END --vartmpをvaroutにくっつける SET @varout = @varout + @vartmp -- カウンタを++する SET @i = @i + 1 END SET @KBS_o_1 = @varout print @KBS_o_1 END
気づいたらKBSトリオの発言をランダムに出力するストアドを作っていた。
僕は何をやっているんだ。
トリオなら引き数に3を、カルテットなら引き数に4を指定すればランダムにKBS発言を行う。
BEGIN~ENDの仕様が本当にどうしようもないのだが、もうちょっとなんとかならないのだろうか。
結果を眺めていたが、KBS発言の成立はなかなか難しいようだ。
もしかすると乱数に偏りがあるのかもしれない。
本当は関数にしたかったが、FUNCTIONだとRAND()が書けなかった。
うーん。
以下、とりあえず当初作ろうと思っていた処理も作っておいた。
殴り書きだ。
CREATE PROCEDURE [dbo].[RANDINT]( --プロシージャ名のかっこの中に引数や返り値を指定する --引き数はこんな感じ @start int ,@end int ,@return int OUTPUT ) --引き数と返り値の設定が終わったらAS AS --BEGIN~ENDでブロックを作る BEGIN SET @return = CONVERT(int,RAND() * ( @end - @start + 1 ) ) + @start ; RETURN ; END
呼び出して結果を確認したい場合はこのように書く。
DECLARE @out int; EXECUTE RANDINT 20,30,@out OUTPUT; PRINT @out;
ビューもついでに書いてみる。
--ビューの作成練習 --DBを指定 use AW4me go --クエリを書き出す --新しく作るときはCREATE, 変えるときはALTER CREATE VIEW V_RandomMonth_Order as --今回はSELECT文 SELECT Soh.[OrderDate] --オーダーの日付 ,COUNT(DISTINCT Cu.[CustomerID]) as CustomerCount --顧客の数 ,COUNT(DISTINCT Cu.[PersonID]) as PersonCount --Personの数 FROM Customer Cu --カスタマーテーブルが基盤 LEFT OUTER JOIN Person pe ON Cu.[PersonID] = Pe.[BusinessEntityID] --パーソンテーブルを結合 INNER JOIN SalesOrderHeader Soh ON Soh.[CustomerID] = Cu.[CustomerID] --オーダー概要テーブルを結合 INNER JOIN SalesOrderDetail Sod ON Sod.[SalesOrderID] = Soh.[SalesOrderID] --オーダー詳細テーブルを結合 where --今日と同じ日付 SUBSTRING(CONVERT(varchar(100),Soh.[OrderDate]),1,2) --オーダーの月 = CONVERT(int,RAND() * 12)+1 -- 1から12のランダムな整数を出力 GROUP BY Soh.[OrderDate] --日ごとに集計
仕事で使う場合は、どうもカーソルとやらを使いこなさなければいけないらしい。
できるかな~。
SQLのサンプルデータベースを材料にViewを作ってみる
システム日付の月と同じ月の発注延べ人数のVIEWを作る
create view tekito as SELECT Soh.[OrderDate] ,COUNT(DISTINCT Cu.[CustomerID]) as CustomerCount ,COUNT(DISTINCT Cu.[PersonID]) as PersonCount FROM Customer Cu LEFT OUTER JOIN Person pe ON Cu.[PersonID] = Pe.[BusinessEntityID] INNER JOIN SalesOrderHeader Soh ON Soh.[CustomerID] = Cu.[CustomerID] INNER JOIN SalesOrderDetail Sod ON Sod.[SalesOrderID] = Soh.[SalesOrderID] where SUBSTRING(CONVERT(varchar(100),Soh.[OrderDate]),1,2) = SUBSTRING(CONVERT(varchar(100),SYSDATETIME()),6,2) GROUP BY Soh.[OrderDate]
このVIEWを元に帳票系をしこしこ出力するバッチとか
ストアドを使って日次更新したテーブルをやっぱりVIEWにしてしこしこ出力するバッチとか
出力したcsvをあれこれするpythonをかいたりしたいです(どれもまだできない)
SQL server 2014用のサンプルDBを入れてみる
参考になったサイト
下のサイトを参考にする。
SQL Serverのサンプルデータベースの移り変わり(Pubs、Northwind、AdventureWorks) - あおきのTechメモ
DL元はこれ。
解凍待ちのあいだ考えたこと
入れる前に解凍する前に不安なのは、
データが綺麗すぎたらやだな、
英語なのはやだな、の2点。
RDBの構造が企業っぽいのは素晴らしいなー、というのはsummaryを見て感じた。
Rのirisとかはキレイすぎて逆に大変よね。
社外にはデータを持ち出せない(当たり前)から、
家ではサンプルを使って分析用のコード書くわけだけど、就業時間中に前処理でハマる。
むしろDirty sampleとかあったりするのかな。こういうニーズは多そう。
あ、できた。この辺は慣れてるから割愛する。っておえー!
解凍後に思ったこと
多いわ!!!!!!!
というわけでとりあえず自分の練習に使えそうなテーブルだけピックアップする。
id:aoki1210さんの記事にスキーマの画像があった。ありがたやありがたや。
- SalesOrderHeader
- [SalesOrderID],[CustomerID]
- SalesOrderDetail
- [SalesOrderID],[ProductID]
- Product
- [ProductID]
- Customer
- [CustomerID],[PersonID]
- Person
- [BusinessEntityID]
とりあえずこの5つのテーブル(と、キー)から始める。
CustomerとPersonの名寄せが[PersonID] <-> [BusinessEntityID]
というのはリアルな感じでなかなかよろしいですな。
AW4meというDBを作って5つのテーブルだけ移植。
Create database AW4me go USE [AdventureWorks2014] select * into [AW4me].[dbo].[SalesOrderHeader] from Sales.SalesOrderHeader select * into [AW4me].[dbo].[SalesOrderDetail] from Sales.SalesOrderDetail select * into [AW4me].[dbo].[Product] from Production.Product select * into [AW4me].[dbo].[Customer] from Sales.Customer select [BusinessEntityID] ,[PersonType] ,[NameStyle] ,[Title] ,[FirstName] ,[MiddleName] ,[LastName] ,[Suffix] ,[EmailPromotion] ,[rowguid] ,[ModifiedDate] into [AW4me].[dbo].[Person] FROM [AdventureWorks2014].[Person].[Person]
Personだけxmlスキーマで持って来れなかったので一部削っている。
結合できるチェックに内部結合のクエリを書く。
USE AW4me SELECT DISTINCT TOP 100 Cu.[CustomerID] ,Sod.[SalesOrderID] ,Pe.[BusinessEntityID] ,Pr.[ProductID] FROM Person pe INNER JOIN Customer Cu ON Cu.[PersonID] = Pe.[BusinessEntityID] INNER JOIN SalesOrderHeader Soh ON Soh.[CustomerID] = Cu.[CustomerID] INNER JOIN SalesOrderDetail Sod ON Sod.[SalesOrderID] = Soh.[SalesOrderID] INNER JOIN Product Pr ON Pr.[ProductID] =Sod.[ProductID]
結果はこう。
IDがbigintっぽいので、不整合なID同士でくっつけても
くっついちゃうんじゃないかとちょっと不安になる。
ただ、ProductやPersonのデータにほどよくNULLがあるのは素晴らしい。
ここからキューブをつくり、エクセルからPowerPivotで引っ張ってくるのが今日中にしたい作業だ。
ローカルでSQL Serverのサービスをスイッチするbatを作る
SQL Serverのサービスをスイッチするbatを作る
下のサイトを参考にした。
【バッチ勉強】サービスの起動・停止状態を取得する方法 | Tipstour
コードはこんな感じ。オリジナリティもなんもないでしゅ。
REM SQLの状態取得開始 sc query MSSQL$(インスタンス名) | findstr STATE | findstr RUNNING > null REM SQLが動いてたら(0)止める(stop) REM SQLが止まってたら(not 1)動かす(start) IF %errorlevel% == 0 ( sc stop MSSQL$(インスタンス名) ) else ( sc start MSSQL$(インスタンス名) ) REM 何かキーを押して下さい... pause
以上。もう一つあるけど長くなったので分ける。
一度アンインストールしたSQL server 2014 expressの再インストールにつまった
昨日の夜から今日にかけて、タイトルの部分で盛大にハマったので書く
ハマりポイント1:サーバーエンジン他1つのインストールに失敗
ハマりポイント2:Management studioまでいってもサーバーへの接続に失敗
原因:以前のサーバー名がManegement studioにデフォルトとして入っていた
こ↑れ↓
サービスが動いていないのかと思って管理ツールまで見に行っても
元気に動いていた 同様の現象を起こす原因が複数あるのが罠だった
賢者には賢者のための罠が、アホにはアホのための罠がある
ぼくはアホでした
賢者のための対策しても意味が無かった
犯人は消したはずのインスタンス?だった
サーバー名がぼくがせっていしたのとちがう
震える手でサーバー名を設定したものに入れなおしたら動いた
ハマりの息抜きにAnacondaもインストールしたものの、
残り時間的にPythonとキューブの勉強の両方はできない
かなしい