MySQLでJSON体験(その1:基本編)

MySQLでのJSONデータの扱い方を、いつか試してみなきゃなぁと思い始めて2年以上が経っているのですが、ようやく、触ってみたいと思うJSONデータに出会ったので、重い腰を上げて試してみました。当方、JSON初心者ですので、このエントリは「お勉強日記」です。

JSONデータ

 先日はじめて、COCOAアプリからの接触確認通知が来ました。このデータはJSON形式のデータとして取り出せるということで、これが今回扱うターゲットデータとなります。
手順としては、

  • COCOAアプリで「情報を保存」ボタンを押して、データを取り出し(私は手軽にメールで自分宛に送信する方法を採りました)
  • 取り出したテキストファイルが JSON 形式となっています。一応、テキストエディタで内容確認
  • MySQL (8.0.29)にテーブルを作成し、INSERT。


今回作成したテーブル:

create table j1 (id integer, j json);

データの登録方法:

INSERT INTO j1 VALUES (1, '(ここにJSONの内容をべたっと(複数行))');

今回のJSONデータ(冒頭部分):

今回の日記の方針・範囲

 今回の日記(1)では、MySQLJSON関数のうち、シンプルに結果が返ってくるものを適当に試してみることにします。自分の見たいデータを取り出して加工して・・・というのは(2)のほうでやります。また、今回は (1)(2) ともに更新系は扱いません。取り出すだけ。

JSON_DEPTH()

JSON入れ子の深さを返す関数。
今回のデータが、元のテキストファイルで6000行以上あり、ちょっと目視だとわからなかった(深度4程度かなと思っていた)けど、どこかに6の深さの部分がきっとあるのでしょう(適当)。

mysql> select json_depth(j) from j1;
+---------------+
| json_depth(j) |
+---------------+
|             6 |
+---------------+

JSON_LENGTH()

JSONの「長さ」を返す関数だそうです。

mysql> select json_length(j) from j1;
+----------------+
| json_length(j) |
+----------------+
|             10 |
+----------------+

6000行もあるのに10ってホントかよ、と当初思ったのですが、要素の繰り返し部分をカットしてみたら、確かにトップレベル(と言って正しいんですかね)の要素は 10個でした。「略」とした部分に大量のデータが含まれています。

{
  "platform": "iOS",
  "platform_version": "15.5",
  "model": "iPhone13,2",
  "device_type": "Physical",
  "app_version": "2.0.1",
  "build_number": "1653370657",
  "en_version": "2",
  "daily_summaries": [ ...(略)... ],
  "exposure_windows": [ ...(略)...  ],
  "exposure_configuration": { ...(略)... }
}

JSON_KEYS()

上記 JSON_DEPTH() では数値が得られたのに対し、JSON_KEYS()は、その内容をJSONの配列にして返してくれる。順序は、どうもキー名の文字列の長さが短い順であるように見える(同じ長さの場合は collation に従う)。

mysql> select json_keys(j) from j1;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| json_keys(j)                                                                                                                                                           |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ["model", "platform", "en_version", "app_version", "device_type", "build_number", "daily_summaries", "exposure_windows", "platform_version", "exposure_configuration"] |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

JSON_TYPE()

JSONデータのタイプを返します。ひとえにJSONデータと言っても、ちょっと複雑なOBJECTやシンプルな配列 ARRAY があるらしい。

mysql> select json_type(j) from j1;
+--------------+
| json_type(j) |
+--------------+
| OBJECT       |
+--------------+


引数には、JSONデータの部分を与えることもできて、その場合は OBJECTやARRAYのほか、抽出したデータの型を返してくれる。以下の場合は、文字列なので、STRING。

mysql> select json_type(JSON_EXTRACT(j, '$.app_version')) jtype from j1;
+--------+
| jtype  |
+--------+
| STRING |
+--------+

JSON_VALID()

JSONとしてどうよ?というのを判定して返してくれる。「1」ならばイイカンジ。まぁ、今回の実行例だと、テーブルに登録できている時点で VALID な JSON であることが分かっているので、この関数は引数として即値(JSON文字列)を与えて使うものなのかな。

mysql> select json_valid(j) from j1;
+---------------+
| json_valid(j) |
+---------------+
|             1 |
+---------------+

JSON要素へのアクセス方法の基本

JSONデータの後ろに ->"" を付けて、""の中に要素を指定する文字列を記述する。
JSONデータのトップ(ルート)が $ で、以降、要素名を深さに応じてドットでつなげていく。配列の要素は [n] (nは正の整数)で指定。

mysql> select j->"$.build_number" from j1;
+---------------------+
| j->"$.build_number" |
+---------------------+
| "1653370657"        |
+---------------------+
mysql> select j->"$.daily_summaries[2].ConfirmedTestSummary" from j1;
+-----------------------------------------------------------------------+
| j->"$.daily_summaries[2].ConfirmedTestSummary"                        |
+-----------------------------------------------------------------------+
| {"ScoreSum": 60.0, "MaximumScore": 60.0, "WeightedDurationSum": 60.0} |
+-----------------------------------------------------------------------+

また、->を使うと、文字列注で \ エスケープする文字がそのまま含まれているのに対して、代わりに ->> を使用するとそれらが省かれるとのこと(引用符も除かれる)。
(ただし SQL モード NO_BACKSLASH_ESCAPES が指定されている場合は動作が異なる)


配列は、以下のようにひとつの要素を指定することもできるし、

mysql> select j->"$.daily_summaries[3].ConfirmedTestSummary.ScoreSum" ss from j1;
+-------+
| ss    |
+-------+
| 840.0 |
+-------+

以下のように、「to」を使って、範囲を指定することもできる。最終要素は数値の代わりに[last]を指定する。

mysql> select j->"$.daily_summaries[3 to 5].ConfirmedTestSummary.ScoreSum" ss from j1;
+---------------------+
| ss                  |
+---------------------+
| [840.0, 60.0, 60.0] |
+---------------------+
mysql> select j->"$.daily_summaries[last].ConfirmedTestSummary.ScoreSum" ss from j1;
+------+
| ss   |
+------+
| 60.0 |
+------+

SQL ServerではじめてのGIS

 本日開催された Database Study Panel vol.1 (ハッシュタグは #dbstudy ) に参加というか視聴させていただきました。

blastengine.connpass.com


その中のお話で、Microsoft SQL ServerLinux版があることを知りました。
docs.microsoft.com


早速試したところ、手元のVM上のUbuntuに、上記サイトの手順でさくっと入れることができました。
マニュアルを軽く眺めると、GIS関連機能が標準で(無料で使用・検証できる express や developerなどでも)使えるようで、これは動かしてみないと、と(パネルディスカッションのお話を聞きながらも、黙々と)試してみた、「最初の第一歩」のお話を書きたいと思います。

インストール

 上記サイトの指示通りに Ubuntu 20.04上にスムーズに入り、動作させることができました。

テーブル定義

 軽くドキュメントみたところ、geometry 型というのはデカルト座標で使うもので、SRSを持つ空間情報型の時は geography 型を使うっぽい感じだったので、その型で定義(まだ理解浅いので誤っているかもしれません)。
 以下の通り、geography型を持つテーブルを定義。

1> CREATE TABLE g1 (id INTEGER, g GEOGRAPHY);
2> go

データの登録

 そもそも geometry と geography があることを分かっていなかったり、ST関数のSTの後ろにアンダースコアがつかなかったり、関数を geography で修飾する必要があったりと、ハマりどころたくさんで試行錯誤の末、以下のようにして登録できました。

1> INSERT INTO g1 VALUES (1, geography::STGeomFromText('POINT(135 35)', 4326));
2> go
(1 rows affected)


1> INSERT INTO g1 VALUES (2, geography::STGeomFromText('LINESTRING(136.12 36.34, 136.12 36.8)', 4326));
2> go
(1 rows affected)

データの確認

 地理情報型のカラムを単純にSELECTしてしまうと、見たい情報が見られないのはMySQLと同じ。ただし、SQL Server では 0xだけ出てきてその後ろは出てくれないんですね。

1> SELECT * FROM g1;
2> go
id          g 
----------- --
          1 0x
          2 0x

(2 rows affected)

 そんなわけで、MySQLで言うところの ST_AsText()を噛ます必要があるわけですが、その使い方もMySQLとは異なりました。カラムに対して後置するようです(カラムのメソッドっぽい書き方)。

1> SELECT id, g.STAsText() FROM g1;
2> go
id                                                                                                                                                                                                                                                                          
----------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
          1 POINT (135 35)                                                                                                                                                                                                                                                  
          2 LINESTRING (136.12 36.34, 136.12 36.8)                                                                                                                                                                                                                          

(2 rows affected)

その他

 SRSについて、SQL Server では JGD2011(SRS=6668)には対応していないようでした。 JGD2000やWGS84は対応しています。

対応しているSRSについては sys.spatial_reference_systems ビューで確認することができます。定義は以下のとおり。
spatial_reference_id に JGD2011のものである 6668 の値のものが存在していませんでした。

11> exec sp_help 'sys.spatial_reference_systems';
12> go
Name                                                                                                                             Owner                                                                                                                            Type                            Created_datetime       
-------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- ------------------------------- -----------------------
spatial_reference_systems                                                                                                        sys                                                                                                                              view                            2022-05-20 13:59:41.520
 
 
Column_name                                                                                                                      Type                                                                                                                             Computed                            Length      Prec  Scale Nullable                            TrimTrailingBlanks                  FixedLenNullInSource                Collation                                                                                                                       
-------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- ----------------------------------- ----------- ----- ----- ----------------------------------- ----------------------------------- ----------------------------------- --------------------------------------------------------------------------------------------------------------------------------
spatial_reference_id                                                                                                             int                                                                                                                              no                                            4 10    0     yes                                 (n/a)                               (n/a)                               NULL                                                                                                                            
authority_name                                                                                                                   nvarchar                                                                                                                         no                                          256             yes                                 (n/a)                               (n/a)                               Latin1_General_CI_AI                                                                                                            
authorized_spatial_reference_id                                                                                                  int                                                                                                                              no                                            4 10    0     yes                                 (n/a)                               (n/a)                               NULL                                                                                                                            
well_known_text                                                                                                                  nvarchar                                                                                                                         no                                         8000             yes                                 (n/a)                               (n/a)                               Latin1_General_CI_AI                                                                                                            
unit_of_measure                                                                                                                  nvarchar                                                                                                                         no                                          256             yes                                 (n/a)                               (n/a)                               Latin1_General_CI_AI                                                                                                            
unit_conversion_factor                                                                                                           float                                                                                                                            no                                            8 53    NULL  yes                                 (n/a)                               (n/a)                               NULL                                                                                                                            
 
Identity                                                                                                                         Seed                                     Increment                                Not For Replication
-------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------- ---------------------------------------- -------------------
No identity column defined.                                                                                                                                          NULL                                     NULL                NULL
 
RowGuidCol                                                                                                                      
--------------------------------------------------------------------------------------------------------------------------------
No rowguidcol column defined.                                                                                                   
 
No constraints are defined on object 'sys.spatial_reference_systems', or you do not have permissions.
 
No foreign keys reference table 'sys.spatial_reference_systems', or you do not have permissions on referencing tables.
The object 'sys.spatial_reference_systems' does not have any indexes, or you do not have permissions.


ちなみに、対応SRS数。

1> SELECT COUNT(*) FROM sys.spatial_reference_systems;
2> go
           
-----------
        393

まとめ

 記述方法や、型の基本的な部分にクセはありましたが(というか私の知識がMySQLベースなので、MySQLのほうがクセである可能性はある)、とりあえずデータを入れたり確認したりすることはできるようになりました。
 あとは、MySQLでは「GIS関係の関数が少ないこと」を課題と感じていましたが、SQL Server では如何ほどの関数が使えるのか、また気が向いたら試してみたいと思います。


memo

接続の仕方もかいておかないと、次やろうとした時には忘れちゃってるw

$ sqlcmd -S localhost -U sa -P (パスワードp2p)

(そもそも sqlcmdの中でどんな命令を使えるかも把握しておかないとなぁ。。 カレントスキーマ知ったり切り替えたりする方法も分かっていないし)

羽生先生99期、ソータ先生9期。ではタイトル戦の少なかった時代の大山先生は何期相当?をSQLを使って雑に求めてみる

 将棋のタイトル獲得期数の話をしていると、「昔はタイトル戦自体が少なかったから」「タイトル戦が3つしかなかった時代の大山先生が今の8つ時代ならいくつ獲っただろうね」という話題になることも多いですね。じゃぁ、年4つの時代に4つとも獲れば(今で言うところの)8個相当、というように重み付けをしてみたらどうなるんだろう、と試してみた日記です。
 もちろんタイトル戦が多くなれば日程が詰まって準備の時間が少なくなるとか疲労がたまるとか、まったく条件が同じというわけではありませんが、お遊びとしてお付き合いいただけたらと思います。

データの用意

 ネットで公開されているタイトル保持者データを、Excelとテキストファイルを駆使してかなり泥臭く手作業で加工してデータを作りました。できあがったデータはこんな感じで、テーブルに登録。今回は集計のお遊びをするだけのデータなので、正規化とかどうでもいいの。

mysql> select * FROM titleholder LIMIT 5;
+------+--------+--------------+------------+
| year | zenkou | kisi_name    | title_name |
+------+--------+--------------+------------+
| 2022 |        | 藤井聡太     | 叡王戦     |
| 2021 |        | 藤井聡太     | 叡王戦     |
| 2020 |        | 豊島将之     | 叡王戦     |
| 2019 |        | 永瀬拓矢     | 叡王戦     |
| 2018 |        | 髙見泰地     | 叡王戦     |
+------+--------+--------------+------------+

 件数は、469件あります。

mysql> SELECT COUNT(*) FROM titleholder;
+----------+
| COUNT(*) |
+----------+
|      469 |
+----------+

年ごとのタイトル戦の数の変遷

 以下のようになります。2022年はまだ3つしか実施されていないので「3」と出ています(その年のタイトル獲得者データなので、未実施のタイトル戦のデータはないため)。なお、棋聖戦が年2回実施されていた時期は、それらを「その年に2個のタイトル」としてカウントしています。1977年がひとつ減ったのは「名人戦未開催」が数字として表れているものですね。
 

mysql> SELECT year, count(*) cnt FROM titleholder GROUP BY year ORDER BY year;
+------+-----+
| year | cnt |
+------+-----+
| 1937 |   1 |
| 1940 |   1 |
| 1942 |   1 |
| 1944 |   1 |
| 1945 |   1 |
| 1947 |   1 |
| 1948 |   1 |
| 1949 |   1 |
| 1950 |   2 |
| 1951 |   3 |
| 1952 |   3 |
| 1953 |   3 |
| 1954 |   3 |
| 1955 |   3 |
| 1956 |   3 |
| 1957 |   3 |
| 1958 |   3 |
| 1959 |   3 |
| 1960 |   4 |
| 1961 |   4 |
| 1962 |   5 |
| 1963 |   6 |
| 1964 |   6 |
| 1965 |   6 |
| 1966 |   6 |
| 1967 |   6 |
| 1968 |   6 |
| 1969 |   6 |
| 1970 |   6 |
| 1971 |   6 |
| 1972 |   6 |
| 1973 |   6 |
| 1974 |   6 |
| 1975 |   7 |
| 1976 |   7 |
| 1977 |   6 |
| 1978 |   7 |
| 1979 |   7 |
| 1980 |   7 |
| 1981 |   7 |
| 1982 |   7 |
| 1983 |   8 |
| 1984 |   8 |
| 1985 |   8 |
| 1986 |   8 |
| 1987 |   8 |
| 1988 |   8 |
| 1989 |   8 |
| 1990 |   8 |
| 1991 |   8 |
| 1992 |   8 |
| 1993 |   8 |
| 1994 |   8 |
| 1995 |   7 |
| 1996 |   7 |
| 1997 |   7 |
| 1998 |   7 |
| 1999 |   7 |
| 2000 |   7 |
| 2001 |   7 |
| 2002 |   7 |
| 2003 |   7 |
| 2004 |   7 |
| 2005 |   7 |
| 2006 |   7 |
| 2007 |   7 |
| 2008 |   7 |
| 2009 |   7 |
| 2010 |   7 |
| 2011 |   7 |
| 2012 |   7 |
| 2013 |   7 |
| 2014 |   7 |
| 2015 |   7 |
| 2016 |   7 |
| 2017 |   7 |
| 2018 |   8 |
| 2019 |   8 |
| 2020 |   8 |
| 2021 |   8 |
| 2022 |   3 |
+------+-----+
81 rows in set (0.00 sec)

今までタイトルホルダーになった人は何人

 シンプルに集計

mysql> SELECT COUNT(DISTINCT kisi_name) ninzu FROM titleholder;
+-------+
| ninzu |
+-------+
|    45 |
+-------+

タイトル獲得期数ランキング

 まずは重み付けを行わずに、実際に獲得したタイトルの数を集計してみます。羽生先生99期、大山先生80期、ソータ先生9期、と正しそうな結果が得られました。

mysql> SELECT kisi_name, COUNT(*) cnt FROM titleholder GROUP BY kisi_name ORDER BY cnt DESC;
+-----------------+-----+
| kisi_name       | cnt |
+-----------------+-----+
| 羽生善治        |  99 |
| 大山康晴        |  80 |
| 中原誠          |  64 |
| 渡辺明          |  31 |
| 谷川浩司        |  27 |
| 米長邦雄        |  19 |
| 佐藤康光        |  13 |
| 森内俊之        |  12 |
| 藤井聡太        |   9 |
| 木村義雄        |   8 |
| 加藤一二三      |   8 |
| 升田幸三        |   7 |
| 南芳一          |   7 |
| 久保利明        |   7 |
| 豊島将之        |   6 |
| 塚田正夫        |   6 |
| 郷田真隆        |   6 |
| 高橋道雄        |   5 |
| 二上達也        |   5 |
| 永瀬拓矢        |   4 |
| 桐山清澄        |   4 |
| 内藤國雄        |   4 |
| 深浦康市        |   3 |
| 藤井猛          |   3 |
| 佐藤天彦        |   3 |
| 丸山忠久        |   3 |
| 屋敷伸之        |   3 |
| 広瀬章人        |   2 |
| 福崎文吾        |   2 |
| 中村修          |   2 |
| 森雞二          |   2 |
| 山田道美        |   2 |
| 糸谷哲郎        |   1 |
| 島朗            |   1 |
| 有吉道夫        |   1 |
| 森安秀光        |   1 |
| 田中寅彦        |   1 |
| 三浦弘行        |   1 |
| 大内延介        |   1 |
| 塚田泰明        |   1 |
| 中村太地        |   1 |
| 斎藤慎太郎      |   1 |
| 菅井竜也        |   1 |
| 木村一基        |   1 |
| 髙見泰地        |   1 |
+-----------------+-----+
45 rows in set (0.00 sec)

重み付けによるスコア付け

 大山先生や中原先生の時代にはタイトル戦が少なかったので、重み付けをしてみることにします。ここでは単純に、その年に存在していたタイトルの数で8を割った数値をスコアとしてカウントすることにします。3つしかタイトルがなかった年はひとつタイトルを獲ると 8/3=2.67 がスコアとなります(その年にタイトルが8個あったと仮定すると 2.67個を獲っていたことに相当するだろう、という単純化です)。なお、2022年のデータが3件しかないので、CASE式を使って強引に8件と見なす処理を加えています。

mysql> WITH tinyear as ( SELECT year, count(*) cnt FROM titleholder GROUP BY year ) 
    -> SELECT kisi_name, SUM(score) * 8 score
    ->   FROM (
    ->     SELECT h.year, h.kisi_name, y.cnt titlesInYear, 
    ->            CASE WHEN h.year=2022 then 1/8
    ->                 ELSE  1/y.cnt END score
    ->       FROM titleholder h
    ->            LEFT OUTER JOIN tinyear y ON (h.year=y.year)
    ->     ) t
    ->  GROUP BY kisi_name
    ->  ORDER BY score DESC
    ->  LIMIT 30;
+-----------------+----------+
| kisi_name       | score    |
+-----------------+----------+
| 大山康晴        | 134.4104 |
| 羽生善治        | 110.5992 |
| 中原誠          |  75.1096 |
| 木村義雄        |  54.6664 |
| 渡辺明          |  33.8640 |
| 谷川浩司        |  28.0024 |
| 塚田正夫        |  26.6656 |
| 米長邦雄        |  20.3360 |
| 升田幸三        |  18.6648 |
| 佐藤康光        |  14.7184 |
| 森内俊之        |  13.7184 |
| 加藤一二三      |   9.3832 |
| 藤井聡太        |   9.0000 |
| 久保利明        |   8.0024 |
| 南芳一          |   7.0000 |
| 郷田真隆        |   6.7160 |
| 二上達也        |   6.3632 |
| 豊島将之        |   6.0000 |
| 内藤國雄        |   5.1440 |
| 高橋道雄        |   5.0000 |
| 桐山清澄        |   4.0000 |
| 永瀬拓矢        |   4.0000 |
| 藤井猛          |   3.4296 |
| 深浦康市        |   3.4296 |
| 丸山忠久        |   3.4296 |
| 佐藤天彦        |   3.2864 |
| 屋敷伸之        |   3.1432 |
| 山田道美        |   2.6672 |
| 森雞二          |   2.1432 |
| 広瀬章人        |   2.1432 |
+-----------------+----------+
30 rows in set (0.01 sec)

 大山先生、134点! 現在の8タイトル時代に大山先生が活躍していれば、134個のタイトルを獲得していた、ということに相当する数字です。すごい!
羽生先生もタイトルが7個の時代が多かったので、110点となりました。ソータ先生はもちろん9点のまま。

順位を数字で入れる

 SQLでこんなこともできるよ、というデモンストレーションの意味しかありませんが、ウィンドウ関数を使用することで、順位を表す数字を結果に含めることができます。
同じスコアの人は同じ順位の数値となっているところがポイントです。

mysql> WITH tinyear as ( SELECT year, count(*) cnt FROM titleholder GROUP BY year ) 
    -> SELECT RANK() OVER (ORDER BY SUM(score) DESC) rnk, 
    ->        kisi_name, SUM(score) * 8 score
    ->   FROM (
    ->     SELECT h.year, h.kisi_name, y.cnt titlesInYear, 
    ->            CASE WHEN h.year=2022 then 1/8
    ->                 ELSE  1/y.cnt END score
    ->       FROM titleholder h
    ->            LEFT OUTER JOIN tinyear y ON (h.year=y.year)
    ->     ) t
    ->  GROUP BY kisi_name
    ->  ORDER BY score DESC
    ->  LIMIT 30;
+-----+-----------------+----------+
| rnk | kisi_name       | score    |
+-----+-----------------+----------+
|   1 | 大山康晴        | 134.4104 |
|   2 | 羽生善治        | 110.5992 |
|   3 | 中原誠          |  75.1096 |
|   4 | 木村義雄        |  54.6664 |
|   5 | 渡辺明          |  33.8640 |
|   6 | 谷川浩司        |  28.0024 |
|   7 | 塚田正夫        |  26.6656 |
|   8 | 米長邦雄        |  20.3360 |
|   9 | 升田幸三        |  18.6648 |
|  10 | 佐藤康光        |  14.7184 |
|  11 | 森内俊之        |  13.7184 |
|  12 | 加藤一二三      |   9.3832 |
|  13 | 藤井聡太        |   9.0000 |
|  14 | 久保利明        |   8.0024 |
|  15 | 南芳一          |   7.0000 |
|  16 | 郷田真隆        |   6.7160 |
|  17 | 二上達也        |   6.3632 |
|  18 | 豊島将之        |   6.0000 |
|  19 | 内藤國雄        |   5.1440 |
|  20 | 高橋道雄        |   5.0000 |
|  21 | 桐山清澄        |   4.0000 |
|  21 | 永瀬拓矢        |   4.0000 |
|  23 | 丸山忠久        |   3.4296 |
|  23 | 藤井猛          |   3.4296 |
|  23 | 深浦康市        |   3.4296 |
|  26 | 佐藤天彦        |   3.2864 |
|  27 | 屋敷伸之        |   3.1432 |
|  28 | 山田道美        |   2.6672 |
|  29 | 森雞二          |   2.1432 |
|  29 | 広瀬章人        |   2.1432 |
+-----+-----------------+----------+
30 rows in set (0.00 sec)

今回の計算ルールで一番得した人は?

 今回、年間のタイトル戦開催回数に応じていわば「嵩上げ」をする計算式を採用しました。この計算方法で一番得した人はだれでしょうか。
じゃん!
取得結果の ratio列の値を見てください。 木村義雄先生 6.8倍、塚田正夫先生4.4倍、升田幸三先生2.67倍あたりが目立ちますね。

 その他のカラムは、cnt 列が獲得期数、cnt_rnk列が獲得期数での順位 です。

mysql> WITH tinyear as ( SELECT year, count(*) cnt FROM titleholder GROUP BY year ) 
    -> SELECT RANK() OVER (ORDER BY SUM(score) DESC) score_rnk, 
    ->        kisi_name, SUM(score) * 8 score,
    ->        COUNT(*) cnt,
    ->        RANK() OVER (ORDER BY COUNT(*) DESC) cnt_rnk,
    ->        SUM(score) * 8/COUNT(*) ratio
    ->        
    ->   FROM (
    ->     SELECT h.year, h.kisi_name, y.cnt titlesInYear, 
    ->            CASE WHEN h.year=2022 then 1/8
    ->                 ELSE  1/y.cnt END score
    ->       FROM titleholder h
    ->            LEFT OUTER JOIN tinyear y ON (h.year=y.year)
    ->     ) t
    ->  GROUP BY kisi_name
    ->  ORDER BY score DESC
    ->  LIMIT 30;
+-----------+-----------------+----------+-----+---------+------------+
| score_rnk | kisi_name       | score    | cnt | cnt_rnk | ratio      |
+-----------+-----------------+----------+-----+---------+------------+
|         1 | 大山康晴        | 134.4104 |  80 |       2 | 1.68013000 |
|         2 | 羽生善治        | 110.5992 |  99 |       1 | 1.11716364 |
|         3 | 中原誠          |  75.1096 |  64 |       3 | 1.17358750 |
|         4 | 木村義雄        |  54.6664 |   8 |      10 | 6.83330000 |
|         5 | 渡辺明          |  33.8640 |  31 |       4 | 1.09238710 |
|         6 | 谷川浩司        |  28.0024 |  27 |       5 | 1.03712593 |
|         7 | 塚田正夫        |  26.6656 |   6 |      15 | 4.44426667 |
|         8 | 米長邦雄        |  20.3360 |  19 |       6 | 1.07031579 |
|         9 | 升田幸三        |  18.6648 |   7 |      12 | 2.66640000 |
|        10 | 佐藤康光        |  14.7184 |  13 |       7 | 1.13218462 |
|        11 | 森内俊之        |  13.7184 |  12 |       8 | 1.14320000 |
|        12 | 加藤一二三      |   9.3832 |   8 |      10 | 1.17290000 |
|        13 | 藤井聡太        |   9.0000 |   9 |       9 | 1.00000000 |
|        14 | 久保利明        |   8.0024 |   7 |      12 | 1.14320000 |
|        15 | 南芳一          |   7.0000 |   7 |      12 | 1.00000000 |
|        16 | 郷田真隆        |   6.7160 |   6 |      15 | 1.11933333 |
|        17 | 二上達也        |   6.3632 |   5 |      18 | 1.27264000 |
|        18 | 豊島将之        |   6.0000 |   6 |      15 | 1.00000000 |
|        19 | 内藤國雄        |   5.1440 |   4 |      20 | 1.28600000 |
|        20 | 高橋道雄        |   5.0000 |   5 |      18 | 1.00000000 |
|        21 | 桐山清澄        |   4.0000 |   4 |      20 | 1.00000000 |
|        21 | 永瀬拓矢        |   4.0000 |   4 |      20 | 1.00000000 |
|        23 | 丸山忠久        |   3.4296 |   3 |      23 | 1.14320000 |
|        23 | 藤井猛          |   3.4296 |   3 |      23 | 1.14320000 |
|        23 | 深浦康市        |   3.4296 |   3 |      23 | 1.14320000 |
|        26 | 佐藤天彦        |   3.2864 |   3 |      23 | 1.09546667 |
|        27 | 屋敷伸之        |   3.1432 |   3 |      23 | 1.04773333 |
|        28 | 山田道美        |   2.6672 |   2 |      28 | 1.33360000 |
|        29 | 森雞二          |   2.1432 |   2 |      28 | 1.07160000 |
|        29 | 広瀬章人        |   2.1432 |   2 |      28 | 1.07160000 |
+-----------+-----------------+----------+-----+---------+------------+
30 rows in set (0.00 sec)

まとめ

 今回の計算ルールで重み付けをした場合でも、羽生先生の 99期というのは大山先生以外には抜かれない大記録であることがわかりました。
データさえあれば、SQLってこのように楽しむこともできるので、ぜひ(これからの方は)学んで、楽しんでいただけたらと思います。ちょっとしたSQLデモでした。




.


.

追記(2022/07/21 14:30) 年ごとの実施タイトル戦リスト

 どの年に何のタイトルがあったのかを知りたいとの声をいただいたので、GROUP_CONCAT()関数を使って出力してみました。

mysql> SELECT year, GROUP_CONCAT(DISTINCT title_name) FROM titleholder GROUP BY year;
+------+---------------------------------------------------------------------------------+
| year | GROUP_CONCAT(DISTINCT title_name)                                               |
+------+---------------------------------------------------------------------------------+
| 1937 | 名人戦                                                                          |
| 1940 | 名人戦                                                                          |
| 1942 | 名人戦                                                                          |
| 1944 | 名人戦                                                                          |
| 1945 | 名人戦                                                                          |
| 1947 | 名人戦                                                                          |
| 1948 | 名人戦                                                                          |
| 1949 | 名人戦                                                                          |
| 1950 | 九段戦,名人戦                                                                   |
| 1951 | 九段戦,名人戦,王将戦                                                            |
| 1952 | 九段戦,名人戦,王将戦                                                            |
| 1953 | 九段戦,名人戦,王将戦                                                            |
| 1954 | 九段戦,名人戦,王将戦                                                            |
| 1955 | 九段戦,名人戦,王将戦                                                            |
| 1956 | 九段戦,名人戦,王将戦                                                            |
| 1957 | 九段戦,名人戦,王将戦                                                            |
| 1958 | 九段戦,名人戦,王将戦                                                            |
| 1959 | 九段戦,名人戦,王将戦                                                            |
| 1960 | 九段戦,名人戦,王位戦,王将戦                                                     |
| 1961 | 九段戦,名人戦,王位戦,王将戦                                                     |
| 1962 | 十段戦,名人戦,棋聖戦,王位戦,王将戦                                              |
| 1963 | 十段戦,名人戦,棋聖戦,王位戦,王将戦                                              |
| 1964 | 十段戦,名人戦,棋聖戦,王位戦,王将戦                                              |
| 1965 | 十段戦,名人戦,棋聖戦,王位戦,王将戦                                              |
| 1966 | 十段戦,名人戦,棋聖戦,王位戦,王将戦                                              |
| 1967 | 十段戦,名人戦,棋聖戦,王位戦,王将戦                                              |
| 1968 | 十段戦,名人戦,棋聖戦,王位戦,王将戦                                              |
| 1969 | 十段戦,名人戦,棋聖戦,王位戦,王将戦                                              |
| 1970 | 十段戦,名人戦,棋聖戦,王位戦,王将戦                                              |
| 1971 | 十段戦,名人戦,棋聖戦,王位戦,王将戦                                              |
| 1972 | 十段戦,名人戦,棋聖戦,王位戦,王将戦                                              |
| 1973 | 十段戦,名人戦,棋聖戦,王位戦,王将戦                                              |
| 1974 | 十段戦,名人戦,棋聖戦,王位戦,王将戦                                              |
| 1975 | 十段戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦                                       |
| 1976 | 十段戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦                                       |
| 1977 | 十段戦,棋王戦,棋聖戦,王位戦,王将戦                                              |
| 1978 | 十段戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦                                       |
| 1979 | 十段戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦                                       |
| 1980 | 十段戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦                                       |
| 1981 | 十段戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦                                       |
| 1982 | 十段戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦                                       |
| 1983 | 十段戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦                                |
| 1984 | 十段戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦                                |
| 1985 | 十段戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦                                |
| 1986 | 十段戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦                                |
| 1987 | 十段戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦                                |
| 1988 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 1989 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 1990 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 1991 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 1992 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 1993 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 1994 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 1995 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 1996 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 1997 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 1998 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 1999 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2000 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2001 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2002 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2003 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2004 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2005 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2006 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2007 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2008 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2009 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2010 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2011 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2012 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2013 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2014 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2015 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2016 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2017 | 名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                                |
| 2018 | 叡王戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                         |
| 2019 | 叡王戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                         |
| 2020 | 叡王戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                         |
| 2021 | 叡王戦,名人戦,棋王戦,棋聖戦,王位戦,王将戦,王座戦,竜王戦                         |
| 2022 | 叡王戦,名人戦,棋聖戦                                                            |
+------+---------------------------------------------------------------------------------+
81 rows in set (0.00 sec)


.
なお、今回はタイトル戦に関する情報は名称しか持っていないので、並び替えることにあまり意味はありませんが、このように ORDER BY を指定することもできます。

SELECT year, GROUP_CONCAT(DISTINCT title_name ORDER BY title_name DESC) FROM titleholder GROUP BY year;

結果(部分):

| 2015 | 竜王戦,王座戦,王将戦,王位戦,棋聖戦,棋王戦,名人戦                                |
| 2016 | 竜王戦,王座戦,王将戦,王位戦,棋聖戦,棋王戦,名人戦                                |
| 2017 | 竜王戦,王座戦,王将戦,王位戦,棋聖戦,棋王戦,名人戦                                |
| 2018 | 竜王戦,王座戦,王将戦,王位戦,棋聖戦,棋王戦,名人戦,叡王戦                         |
| 2019 | 竜王戦,王座戦,王将戦,王位戦,棋聖戦,棋王戦,名人戦,叡王戦                         |

MySQL: desc は explain !?

 別件の確認のために MySQL マニュアルを見ていて、とんでもない事に気づいてしまいました。

The DESCRIBE and EXPLAIN statements are synonyms.

MySQL :: MySQL 8.0 Reference Manual :: 13.8.2 EXPLAIN Statement

 ご存じの通り、MySQL において、DESC (または DESCRIBE)はテーブル定義を確認するときに使用するもの、EXPLAINは実行計画を確認するものとして知られています。こいつらがシノニムだっただと・・・・・!!|???

.
私の知ってる DESCの使い方:

mysql> DESC sample01;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int         | NO   | PRI | NULL    |       |
| code  | char(2)     | YES  |     | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)


.
これがなんと、EXPLAINでも同じらしい(シノニム)。

mysql> EXPLAIN sample01;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int         | NO   | PRI | NULL    |       |
| code  | char(2)     | YES  |     | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.01 sec)

ぉぉ・・・・・

.


私の知っている EXPLAIN の使い方:

mysql> EXPLAIN SELECT COUNT(*) FROM sample01 WHERE id>3;
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type | table    | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | sample01 | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    4 |   100.00 | Using where; Using index |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)


これがDESCでも同じ(シノニム)だったなんて・・・・:

mysql> DESC SELECT COUNT(*) FROM sample01 WHERE id>3;
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type | table    | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | sample01 | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    4 |   100.00 | Using where; Using index |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)


.
知っている人は知っている。知らないひとはちょっと驚く、小ネタでした。



.

MySQL :: MySQL 8.0 Reference Manual :: 13.8.2 EXPLAIN Statement より:

{EXPLAIN | DESCRIBE | DESC}
    tbl_name [col_name | wild]

{EXPLAIN | DESCRIBE | DESC}
    [explain_type]
    {explainable_stmt | FOR CONNECTION connection_id}

{EXPLAIN | DESCRIBE | DESC} ANALYZE [FORMAT = TREE] select_statement

explain_type: {
    FORMAT = format_name
}

format_name: {
    TRADITIONAL
  | JSON
  | TREE
}

explainable_stmt: {
    SELECT statement
  | TABLE statement
  | DELETE statement
  | INSERT statement
  | REPLACE statement
  | UPDATE statement

The DESCRIBE and EXPLAIN statements are synonyms. In practice, the DESCRIBE keyword is more often used to obtain information about table structure, whereas EXPLAIN is used to obtain a query execution plan (that is, an explanation of how MySQL would execute a query).

COUNT()関数の中身の書き方による動作の違い

 自分では「知っている」と思っていても、会話の中でふと自信がなくなることってありますよね。 自分の知識として「常識」になっているけど、気づけば10年以上も意識して試したことのないものとか。
表題のテーマが、今回の私のソレです。

 ということで、COUNT(*) とか COUNT(カラム名) とかの書き方で動作がどう変わるのか改めて試した結果を整理してみた、という、初心者向きの内容です。


どんな書き方があるか

COUNT(*)      
COUNT(カラム名)  
COUNT(DISTINCT カラム名)

 これらはそのカラムが NULL の行が存在するか否かで結果が変わります。


サンプルデータ

CREATE TABLE sample01 (id integer primary key, code char(2), name varchar(20));

INSERT INTO sample01 VALUES (1, '21', '名前1');
INSERT INTO sample01 VALUES (2, '22', '名前2');
INSERT INTO sample01 VALUES (3, null, '名前3');
INSERT INTO sample01 VALUES (4, '22', '名前4');
INSERT INTO sample01 VALUES (5, '22', '名前5');
INSERT INTO sample01 VALUES (6, '21', '名前6');
INSERT INTO sample01 VALUES (7, null, '名前7');
mysql> SELECT * FROM sample01;
+----+------+---------+
| id | code | name    |
+----+------+---------+
|  1 | 21   | 名前1   |
|  2 | 22   | 名前2   |
|  3 | NULL | 名前3   |
|  4 | 22   | 名前4   |
|  5 | 22   | 名前5   |
|  6 | 21   | 名前6   |
|  7 | NULL | 名前7   |
+----+------+---------+
7 rows in set (0.01 sec)

COUNT(*)

 COUNT(*) は、条件に該当する全行の数を返します。今回はWHEREを指定していないのでテーブル全件の7件が返ります。もちろん WHERE を指定すればそれに合致する行の数が返ります。

mysql> SELECT COUNT(*) FROM sample01;
+----------+
| COUNT(*) |
+----------+
|        7 |
+----------+
1 row in set (0.00 sec)

COUNT(カラム名)

 COUNT()の中にカラム名を指定した場合、そのカラムに有効な値がセットされているものの行数が返ります。もう少し具体的に言うと「NULLでないものの行数」が返ります。

mysql> SELECT COUNT(code) FROM sample01;
+-------------+
| COUNT(code) |
+-------------+
|           5 |
+-------------+
1 row in set (0.00 sec)

 このことは、もちろんちゃんと知っていればこのような動作であることを見ただけで理解できるのですが、一般には直感的だとは私は思えません。
NULLを除外した件数を取得したい場合は、私ならまず以下のように条件として明示することを勧めたいところです。
(他の取得カラムや抽出条件との関係で、意図して COUNT(カラム名)を使う場合もあります)

mysql> SELECT COUNT(*) FROM sample01 WHERE code IS NOT NULL;
+----------+
| COUNT(*) |
+----------+
|        5 |
+----------+
1 row in set (0.00 sec)

COUNT(DISTINCT カラム名)

 COUNT()の中に DISTINCT カラム名 を指定した場合は、指定されたカラムのユニーク項目数を返します。NULLは含まれません。
以下のとおり「2」(codeの値が 21 と 22の2種類あるので)と返ってくることが確認できました。

mysql> SELECT COUNT(DISTINCT code) FROM sample01;
+----------------------+
| COUNT(distinct code) |
+----------------------+
|                    2 |
+----------------------+
1 row in set (0.00 sec)

余談

 実は「*」には "その行がある"、という程度の意味しかなくて(私の理解が間違っていたらご指摘ください>ガチ勢のみまさま)、つまり行に相当する何かあればカウント対象になるわけです。ですから、固定文字列だったり適当な数字だったりを指定しても COUNT(*) と同様の動作をします。誰も何の得もしないので、よい子はまねをしないように。

mysql> SELECT COUNT('test') FROM sample01;
+---------------+
| COUNT('test') |
+---------------+
|             7 |
+---------------+
1 row in set (0.00 sec)

mysql> SELECT COUNT(3.141592) FROM sample01;
+-----------------+
| COUNT(3.141592) |
+-----------------+
|               7 |
+-----------------+
1 row in set (0.00 sec)

.



.

追記(2022/07/20 00:15)

 さっそく yoku0825さんからツッコミをいただきました。

EXPLAIN(5.6とそれ以前はEXTENDED)からのSHOW WARNINGSで見ると、COUNT(*)はCOUNT(1)にオプティマイズされてるっぽいですよね


 確かに、EXPLAIN 取ってみると、warning が1件出ている! 
(関係ないけど Extra 列に最近マイブームのUsingが出ています。本当に関係ない)

mysql> explain SELECT COUNT(*) FROM sample01;
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table    | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | sample01 | NULL       | index | NULL          | PRIMARY | 4       | NULL |    7 |   100.00 | Using index |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)


 warningの中身を見てみると・・・・・・

mysql> show warnings;
+-------+------+---------------------------------------------------------------------+
| Level | Code | Message                                                             |
+-------+------+---------------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select count(0) AS `COUNT(*)` from `test`.`sample01` |
+-------+------+---------------------------------------------------------------------+
1 row in set (0.00 sec)

COUNT(*) が COUNT(0)になってるー!!!!


一方の COUNT('TEST') (よい子はまねしちゃいけないやつ)は、、、、、

mysql> desc SELECT COUNT('TEST') FROM sample01;
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table    | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | sample01 | NULL       | index | NULL          | PRIMARY | 4       | NULL |    7 |   100.00 | Using index |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> show warnings;
+-------+------+-------------------------------------------------------------------------------+
| Level | Code | Message                                                                       |
+-------+------+-------------------------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select count('TEST') AS `COUNT('TEST')` from `test`.`sample01` |
+-------+------+-------------------------------------------------------------------------------+
1 row in set (0.00 sec)


・・・そのまま(笑)。

.


.

追記(2022/07/20 09:00)

 とみたまさひろさんから、COUNT()の引数は「式」であるとの指摘をいただきました。なるほど!

 なんか面白いことできないかと試みたのですが、以下、code列の値が21であるものの件数を得る試みですが、マッチしない場合の結果は NULL ではなく false(というか0) になるだけなので、意図失敗。

mysql> SELECT COUNT(code=21) FROM sample01;
+----------------+
| COUNT(code=21) |
+----------------+
|              5 |
+----------------+
1 row in set (0.01 sec)

 やりたければ、こうなんだろうけど、ここまでやらなくてもねぇ(笑)。

mysql> SELECT COUNT(CASE code WHEN 21 THEN true ELSE null END) FROM sample01;
+--------------------------------------------------+
| COUNT(CASE code WHEN 21 THEN true ELSE null END) |
+--------------------------------------------------+
|                                                2 |
+--------------------------------------------------+
1 row in set (0.00 sec)

TiDB User Day 2022に参加してきた話

 開催から2週間近くが経ってしまいましたが、「TiDB User Day」が開催され、参加してきました。非常に有意義かつ楽しい時間だったので、日記にしたためておきたいと思います。なお、当方、分散DBについてはまだよく分かっていないので、技術的な詳しい内容は書きません。「感想文」または「日記」としてご覧いただければと思います。


 当日の模様については、以下のリンク先に動画および講演資料が公開されているので、私の日記なんかよりもこちらを参考にしてください。
pingcap.co.jp



 さて、今回の TiDB User Day 2002。私にとって2つの視点で、興奮する時間となりました。それぞれ書きます。

久々のオフラインで楽しかった

 小学生の作文みたいなタイトルですが、正直な心境も、小学生のように純粋にただただ楽しい空間に身を置くことができた喜びから出た言葉です。
この2年間、一切参加することができなかった「オフライン会場」でのセミナー参加。 待ち焦がれたこの時間。 この日記を書いている時点では第七波が急激に進行中でもうオフライン開催できる状況でなく、結果としてまさにギリギリの開催タイミングとなりました。
 会場内で聞こえてくる検証状況の会話、疑問点を述べる人とそれに対して自分の知っている知識を披露する会話。各自が試した結果の情報を交換しあう会話。それらが会場のあちらこちらで同時多発で繰り広げられる空間。私自身はまだ検証もしておらず、どちらかというとアウェー気味な中にポツンと居りましたが、この空間の中に居られるだけで、言いようのない幸せを感じました。 やっぱり私はオフラインの会合が好きです。 久々の感覚に「これだよ!これ!!!!」と何度も心の中で叫んでおりました。
 2年間ほど、オンラインでこの雰囲気を作れないかと模索してきたけど、やはりオフラインにかなう空間は、ありませんね。開催ありがとうございました。

TiDB 面白いかも

 今年に入ってから「何かおもしろそうなモノだ」という嗅覚が反応したので注目していましたが、正直なところ、使いどころや特徴など、今ひとつよく分かっていませんでした。今回のイベントで、そのあたりを登壇者の皆さんから聞かせていただき、朧気ながらイメージがわいてきました。まだ出たての、これから時間をかけて検証して、その後に実戦投入していくツール、くらいに感じていたのが、各社さんともかなり検証は進んでいて実戦投入も始まっているということで、自分の情報感覚のニブさを恥じ入る次第。
 勝手に「シャーディング疲れのあなたに」というキャッチコピーを思いついたとおり、データ量が非常に多くてシャーディングなどのテクニックを駆使ししてなんとか速度低下を抑えていたものが、TiDBをうまく使えばもう少しシンプルに解決できる、と理解しました。もちろん分散DBであるが故のコツ(たとえば auto_increment は無理なので別の方法に置き換える必要があるなど)があり、その辺りのノウハウがこれから出そろってくると、(今使っている先進ユーザの次に来る)後追いユーザたちも安心して検討を始められるようになるのかなと感じました。

 お話を聞いて、とにかくこれは楽しそうだなと強烈に感じていることを自覚できたのも、今回の参加で得たものです。 一部の先進ユーザを除けば、全体としてはまだツールのクセを見抜き、ノウハウを交換しながら自社に最適な導入を目指しているフェーズですから、この楽しそうな中に身を置きたい!と、かなり本気で感じました。 導入を検討の会社様、検証チームのメンバをお探しの会社様、坂井を呼んでもいいなと思われましたら、ぜひお声がけください! 私自身も現時点では TiDBのノウハウを持つものではありませんが、一緒に検証、検討していければと思います。絶対、いま一番楽しいフェーズなので、ご一緒できましたら幸いです。

少しは具体的な内容も書く

 冒頭で述べた「2つの視点で」は書き切ったのですが、あまりに内容に関する内容がないので(笑)、少しだけ印象に残った話題を。

 まず、イベント冒頭の CTOのEdさんのお話に引き込まれました。ビデオメッセージだったので、当たり障りのない社交辞令的ご挨拶をされるだけかと思っていたら、本当に楽しそうに自分たちの歩んできた道や目指している世界などを語ってくれたのが印象的でした。司会者の説明によると、「10分枠なのに、最初は20分以上も生き生きと技術話をされて、録り直しになった」とか。 フルバージョンも聞いてみたいところです。
 あとは、「INSERTが速い」という評価をされている方がいる一方で、分散DBであるが故にすべての処理にレイテンシが乗っかる(10-20ms)ことを気にする発言もありました。今MySQLで動作しているものすべてを TiDB化するのではなく、特に低レイテンシが重要である部分はMySQLが最適であり、使い分けをしていくとのことです。
 それから、「TiDB」が何を指しているのか、あるいは、どういう環境で動作しているのかが、ようやく今回分かりました。OSSとして公開されていて、自分のサーバ群にインストールして使える「TiDB」と、AWSなどの上でマネジドで動作している「TiDB Cloud」の2本があると。 この2つの話を混ぜて理解していたので「自前でインストールできるのにマネジド???」と、ずっと混乱していました。 



 ということで、改めましてコロナギリギリのタイミングの中、オフラインを含むイベントを開催してくださり、また素晴らしい講演者のみなさまにお話をしていただく場を作っていただきありがとうございました。 イベントの運営進行、そして会場全体に亘って配慮の行き届いた、素晴らしいイベントに参加させていただけたと感じています。
そして、自分自身の力及ぶか及ばずかは分かりませんが、久々に「案件に関わってみたい」とワクワクするものに出会えた気がします。「これやってみたい!」とアツアツの人がチームに居ることは大きいと思いますので、ご興味の会社さん、ご検討中の会社さん、ぜひお引き合いをお待ちしております!

オンラインでMySQLとかの話をわいわいする「今夜も生でMySQL(仮題)」を開催しました

長らくオフラインのイベントを開催できないと、ざっくばらんにわいわいと語り合うあの雰囲気を忘れそうにもなり、また、恋しくもなります。
オンラインで、何か少しでもあの雰囲気を作ることはできないか、と悩んだ結果、こんなイベントをやってみています。

mysql.connpass.com


Discordを使って、みんなで集まる時間を決めて、あとは話題もなにも事前には決めない。 集まった人で、たまたまそのときに話題になったもので盛り上がったり盛り下がったりする、飲み会のあの雰囲気になれたらいいなぁと、先月から開催してみています。 まずは月1回ずつ、少なくとも3回は続けてみるつもりでやっていますが、意外と盛り上がらないなー、案外つまんないなーと思ったらやめてしまうかもしれません(笑)。 それくらい緩い感じで、「時間と場所だけを案内、提供する」イベントが果たして成立するのか、試みているところです。

なので「いつか参加してみようかなー」と思っている方は、「いつか」が来たときにはもうやめているかもしれませんので、ぜひ来月は覗きに来ていただけたら嬉しいです。


ここまで2回、ぽつぽつとお集まりいただき、それなりに話題は尽きずに盛り上がっているかなと思います。ただ、やっぱり幹事として話題フリをしなきゃ!とちょっと頑張っているところはあるので、どうしても私が話している時間が長くなっちゃうし、目指しているのは、幹事が離席していても気づかれないくらいに勝手に盛り上がっている場なので、まだまだ遠いなぁと頭を悩ませている次第です。


と言いながら、私自身は私が聞きたい話をいっぱい聞けて、とても楽しんでいるのですが、参加されていた皆さんそれで良かったのかなー、他の話題をしたいのに言い出しにくかったりしないかなーと、幹事として悩んだりもしています。



第2回はこんな話が展開されました。雰囲気で会話して、雰囲気で記憶しているので、ちょっと違うところもあるかもしれません。

MySQL Reference Manual 差分ウォッチによる未来先取り(8.0.30や8.0.31での変更予定を見てわいわい)
Software Design 誌へのとみたさんの「MySQLで学ぶ文字コード」を基にした話題(異字体セレクタセレクタ便利!)
・立ち上げたMySQLサーバにリモートから接続できない、とお悩みの参加者の問題解決にわいわい
MySQL関係ないけど、ニューノートPC買ったよからセットアップの話題とか


なんだか悩みだらけの日記ですが、オフラインで一緒に過ごす時間を作りにくい今の時代、情報交換の場が激減している、少し固い言葉で言うと情報流通が止まってしまっていることを憂いています。 できることを少しでも、と色々試みていますので、共感してくださる方はぜひ手伝ってください!



「生マイ」は8月もたぶんやりますので、興味もたれた方の参加をお待ちしています!
8月にはおそらく「MySQL 8.0.30のリリースノートでわいわい言う勉強会」も開催されると思います。
セミナー系イベントも全然できておらず、できることならそろそろやりたいので、スピーカー絶賛募集です。