優柔不断なプログラマの日記

思いつきでポチポチ書きます

(Windows)Windowsでgrepする = findstr

2008年01月11日 | プログラミング

Windowsにはgrepがありません。
うっかりこういうことをしてしまう。

C:>net start |grep -i tel
'grep' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。

いつもなら、あ~無いんだった。と別の方法に逃げるところですが、今日はついムキになってgrep探しの旅です。
いくらGUIなWindowsでも、grepぐらいある(誰かが作っている)のは、うすうす気づいていました。ググルと、トップにvectorヒット。「Windows版Grep(コマンドライン用)」。お、いきなりnot GUIなツールがヒットです。
説明を読んでみる。“正規表現”“再帰”。うむ、これでよさそう。早速ダウンロードして解決、と思いきや。

パイプが使えない。

なんでだよー。パイプで使うよね。使えなきゃダメっす。ということで却下。

他にもGNU grepってあるからこれか!と思い試すとインストールした上、.netが必要。そしてまたしてもパイプ非対応。
うむむー、パイプ使う人は小数派なのか・・・

グーグル結果を眺めていると、ありました。

findstrで正規表現を検索する
http://www.atmarkit.co.jp/fwin2k/win2ktips/511findstr/findstr.html

まんまgrepじゃないですか。

C:>net start|findstr -i tel
   Background Intelligent Transfer Service
   Telephony
   Telnet

解決しました。


 


(MSSQL Server)ストアドファンクション でパラメータ省略

2008年01月10日 | プログラミング

今日のお題
SQLサーバーで、文字列を整形する関数を作る。

どうやら、ストアドプロシージャ(PROCEDURE)は戻り値として、数値しか返却できないようだ。いろいろなデータ型を返却したいときにはストアドファンクション(FUNCTION)を使うようだ。ちょっと練習してみる。

ストアドファンクション作成
CREATE FUNCTION testfunc
( @str nvarchar(max)
 , @len int = 3
)
RETURNS nvarchar(max)
AS
BEGIN
    RETURN LEFT(@str,@len);
END;

第2引数を省略して実行
select dbo.testfunc('ABCDEFG')

メッセージ 313、レベル 16、状態 2、行 1
プロシージャまたは関数 dbo.testfunc に指定された引数が不足しています。


えぇ?エラー?なぜ?ちゃんと @lenにdefault値を指定しているのに…
ググるとストアドファンクションソースはいくつもヒットするが、default値指定しているfunctionが見当たらない…
途方に暮れ、Books Onlineと同じだよなーと思いつつ、当然のようにヒットしているMSDNを読む。

http://msdn2.microsoft.com/ja-jp/library/ms186755.aspx

[ = default ]
パラメータの既定値です。default 値が定義されている場合は、パラメータに値を指定せずに関数を実行できます。

そうだよね。できるはず。でも省略したらエラーになるんです。

関数のパラメータに既定値がある場合に、既定値を取得する目的でその関数を呼び出すときは、DEFAULT キーワードを指定する必要があります。この動作は、ストアド プロシージャで既定値を持つパラメータを使用する場合とは異なります。ストアド プロシージャの場合は、パラメータを省略すると既定値が暗黙的に使用されます。

え?“DEFAULT”って記述するってこと?

正しい第2引数を省略して実行
select dbo.testfunc('ABCDEFG', DEFAULT)
---------------------------------------
ABC

(1 行処理されました)


できました。なぜPROCEDUREと同じじゃないんでしょう…まさかオーバーロードできる…わけないですね。


 


(MSSQL Server)UPSERTストアドサンプル

2008年01月09日 | プログラミング

データベーステーブルへデータを登録する際、同じキーだったらUPDATE、同じキーが無ければINSERTして欲しい場面って、たまにありますよね。

UPDATE+INSERTで、俗に"UPSERT"とか言うようです。
ORACLEではMERGE INTOって構文があるのでそれでOKなんですが、今いじっているMS SQL Server 2005では…どうやらなさそう。(ひょっとしたらある?)
まあ、触り初めだし、練習がてらストアドでも作ってみる。

●サンプルテーブル準備
CREATE TABLE shouhin
( shouhin_code int
, shouhin_name nvarchar(100)
, shouhin_bunrui_code int
,CONSTRAINT "PK_shouhin" PRIMARY KEY CLUSTERED
    (shouhin_code ASC)
);

●ストアド作成
CREATE PROCEDURE upsert_shouhin
     @shouhin_code int = NULL
    ,@shouhin_name nvarchar(100) = NULL
    ,@shouhin_bunrui_code int = NULL
AS
DECLARE
      @sqlstr   NVARCHAR(max)
    , @setstr   NVARCHAR(max)
    , @paramstr NVARCHAR(max)
;
-- ■■パラメータのチェック
    IF @shouhin_code IS NULL
        RETURN -1;
-- ■■UPDATEしてみる
    -- ベースとなるUPDATE SQL
    SET @sqlstr = '
        UPDATE shouhin
          {setstring}
         WHERE shouhin_code = @shouhin_code
    ';
-- ■UPDATE文文字列を組み立てる(指定されたパラメータだけ更新する)
    SET @setstr = '';
    IF @shouhin_name IS NOT NULL
        SET @setstr = @setstr + 'shouhin_name = @shouhin_name,';
    IF @shouhin_bunrui_code IS NOT NULL
        SET @setstr = @setstr + 'shouhin_bunrui_code = @shouhin_bunrui_code,';
-- ■"SET A=1,B=2"の文字列に整形し、ベースSQLにはめ込み
    IF RIGHT(@setstr, 1) = ','
        SET @setstr = LEFT(@setstr, LEN(@setstr) - 1);
    SET @setstr = 'SET ' + RTRIM(@setstr);
    SET @sqlstr = REPLACE(@sqlstr, '{setstring}', @setstr);
-- ■完成したUPDATE SQLテキストを実行する
    SET @paramstr = N'
          @shouhin_code int
        , @shouhin_name nvarchar(100)
        , @shouhin_bunrui_code int
    ';
    EXECUTE sp_executesql @sqlstr
                        , @paramstr
                        , @shouhin_code = @shouhin_code
                        , @shouhin_name = @shouhin_name
                        , @shouhin_bunrui_code = @shouhin_bunrui_code
    ;
-- ■■UPDATE対象が無かった場合はINSERTする
    IF @@ROWCOUNT < 1
        INSERT INTO shouhin
            ( shouhin_code, shouhin_name, shouhin_bunrui_code)
            VALUES ( @shouhin_code, @shouhin_name, @shouhin_bunrui_code);
-- ROWCOUNTでも返しとく
    RETURN @@ROWCOUNT;

 

●実行してみる
exec upsert_shouhin 101,'鉛筆HB',1;

exec upsert_shouhin 101,'鉛筆HB 1ダース',99;
exec upsert_shouhin 102,'鉛筆2B 1ダース',99;


動作は一応予定通り。いきなりUPDATEしちゃうのは乱暴かなあ。でも、SELECTで確認するとSELECT+UPDATA/INSERTで常に2回SQL発行しちゃうんですよね。いきなりUPDATEだと、UPDATEにヒットしたら1回で終わります。まあ、この辺はそれぞれでしょうか。

それより問題なのは、冗長すぎるソースと、カラムの値をNULLに更新できないことですね。
汎用的にUPDATEする場面を考えたとき、必ずしも全部のカラム値を更新するとは限らないですよね。

なので、更新したくないカラムパラメータを省略可能にしたかったのですが、省略値=NULLにしたため、呼出元がNULLで渡したのか、省略したのか判断不能になってしまいました。でも、パラメータを省略するにはデフォルト値を指定しろって書いてあるしなあ。この辺りは追々調査必要ですね。(←大体プロジェクトが終わるころに判明して、修正できずそのままになってしまうパターン)

また、省略したカラムをUPDATE SQL文から除去するため、文字列組み立てする感じになり、とても冗長に・・・カラムの多いテーブルだと長くなりそう。

でも、まあ、このレイヤなら冗長でもいいか。