ChromeとEdgeでURL欄からコピーしたURLテキストの持ち方が違うという話

今回はGoogle ChromeとMicrosoft EdgeでURL欄からURLをコピーした時の挙動の違いを紹介する短いネタです。
例えば MySQL のサイトを開いて、ブラウザのURL欄からそのURLをコピーしたときにクリップボードに入るものを比較してみます。
Google Chromeの場合
- 0
- type
text/plain - value
https://www.mysql.com/
- type
これだけです。
当然貼り付け時もhttps://www.mysql.com/だけが貼り付きます。
Microsoft Edgeの場合
- 0
- type
text/plain - value
https://www.mysql.com/
- type
- 1
- type
text/html - value
<a href="https://www.mysql.com/">MySQL</a>
- type
となっています。 (aタグのテキスト部分はページのtitle要素が拾われます。)
貼り付け時の動作
- 書式貼り付けが可能なUI(Office全般など)に貼り付けた場合、Edgeからの貼り付けの場合「MySQL」の文字に
https://www.mysql.com/へのリンクが張られます。 - 書式貼り付けできないUIの場合はtext/plainの方の
https://www.mysql.com/の文字列が張り付きます。- ブラウザのURL欄は書式貼り付け不可なので、URLのみが張り付くというわけです。
アレンジ
さて、これを踏まえてMarkdownエディタをカスタマイズするのも面白いです。
- 貼り付けイベントを検知
- それが
<a href="xxxx">yyyy</a>の形式のテキストだったら「Markdown書式で貼り付けるか」をダイアログで確認する。 - 「OK」だったら正規表現で構文解析をしてhref属性の値とテキストをMarkdownの書式に配置しなおしてカーソル位置に挿入する。貼り付けイベント自体は取り消してしまう。
- 「キャンセル」だったら加工せずに貼り付ける。
ビジネスチャットアプリのMattermostクライアントが同等の機能をすでに実装してました。
私もひとつ前に紹介した「タスク管理ツール」に実装してみました。
SharePointOnlineのURLがアホみたいに長いのですが、これで気兼ねなくペタペタできます。
dupont.hatenablog.jp
私がたどり着いたgtdベースのタスク管理の話
目次

本エントリの概要
一般企業の情シス系職種サラリーマンとしてタスク管理について工夫してきた内容を共有します。
※本エントリはあくまで個人のタスク管理に主眼を置いております。多人数参加のプロジェクトのタスク管理はプロジェクトマネジメントの手法を使った方がいいです。
思いついたことを時々更新するかもしれません。
背景・目的
- タスク管理
- 複数のシステム運用や保守のほか庶務的業務やら職場活動やら比較的幅広い業務領域をカバーし、多種多様なタスクが舞い込んでは積み上げて処理をする日々です。
- なのでタスク管理は必須です。
- システム運用記録
- 複数の中/小規模な内製のシステムを個人商店的に運用/保守しており、それを一元的に記録すべきでした。
- 日報
- 日々の業務報告をタイトルのみでよいので簡単に一覧にして報告するという指示があります。
- 思い
- これらをストレスと時間をかけずにこなしたい。
- プライベートの時間に仕事を持ち込みたくない。
ベストプラクティス
タスク管理手法に「gtd🄬」というものがあります。
書籍も買って学習し、これが基本コンセプトになりました。
もっとも重要なポイントは「すべて脳から追い出す」です。
手法についてはアレンジをたくさん加えています。
gtdとは
- 「gtd🄬」について知りたい方は公式サイトや書籍をどうぞ。
- ただし2冊目以降は「手法を商用ビジネスにしている」感じで、ちょっと無理があるように感じてます。
- なので個人的には1冊目が一番お勧め。古いので紙を使う前提の運用で説明されてますがコンセプトはツールに依存しません。
- gtd-japan.jp
- Amazon 「はじめてのGTD ストレスフリーの整理術」 (アフィリエイトなしリンク)
私の運用
タスクの管理
- タスクが発生したら基本的にすべて「タスク管理ツール」(後述)に登録します。
- ここでいう「タスク」は以下のようなものです。
- 指示された業務すべて(庶務的なものも含めて)
- 運用システムでの問い合わせ対応
- 同僚の困りごと支援
- 学習したいこと
- 職場でやりたいこと
- タスクごとの進捗や処理内容、調査結果などもそのタスクエントリに日付と一緒に追記してゆきます。
inbox
- メールの受信トレイはgtdでいう「inbox」として扱い、タスクとなるメールはタスク管理ツールに転記して仕訳けて基本的に受信トレイは空にしておきます。
- チャットもタスクとなるメッセージは未読にもどしておき、タスク管理ツールに転記してから既読にします。
システム運用記録
- システム運用記録も兼ねるので、1分以内に済んでしまうような問い合わせ対応なども事後登録します。(ここはgtdに準拠していません。)
- できるだけ5W1Hでやったことを記録します。ITILのサービス管理の考え方です。
納期
- タスクの表に出す納期は依頼や指示の最終納期ではなく、「分解したタスクの次工程をいつまでに」を考えてプロットした日付です。
- たとえば「個人の休みの都合」や「ほかのタスクとの兼ね合い」を考慮して日付を調整します。
- そのため重要なのは、タスクを登録するときに「タスク完了の状態をイメージすること」と、「そこまでに必要な工程をざっくりと洗い出すこと」です。
メモリクリアとストレス解放
- 進捗をタスクエントリに入力しきって、残タスクの納期を調整してからその日の業務を終了します。
- これをすることで業務終了後はすべて忘れてよい安心感を得られ、ストレスから解放されます。
- また、翌営業日に仕事を始める際に「どこまでやったか思い出す暖機運転」が早いです。
タスク管理ツールの選定と実装
- Excel、Outlook、todo系アプリ、redmineなど、ツールをいろいろ見ましたがどれも「帯に短し襷に長し」、かゆいところに手が届かないもどかしさがあり、結局PHP+JavaScript+MySQLで自作しました。
自作したタスク管理ツールの機能
- 一覧は優先順位順
- 次工程納期や優先度で並ぶようにする
- これを業務開始時に見てその日のやることを決める
- もちろん検索できる
- 自由なキーワードで検索ができる。
- 日付検索すればその日に何をやったか一覧化できるので、それを日報に転記する。
- タスクごとに詳細記録できる
- タイトルだけでなく、「指示/要請」「実施内容」などをテキストで記述できる
- ライトにたくさん簡単に書ける入力支援
- プレーンテキストまたはMarkdownでバリバリ書ける
- インデントはTABキーで入力できるよう、キー制御
- 編集画面以外では構造をHTMLレンダリングする
- ショートカットキーで文中に現在日付を入れられる
- URLは自動リンク
- タブ区切り複数行貼り付け時はMarkdown表形式として貼り付け可能にする
- プレーンテキストまたはMarkdownでバリバリ書ける
- タスクエントリごとにユニークなIDを発行できる
- 変更履歴管理のコミットにこのIDを記述し、記録と紐づける
- タスクエントリを複製できる
- 定型作業の記録はこれを使えば楽
- 親子関係を作れる
- 子エントリを作成して分解したタスクをエントリに切り分ける
ポイント
記録やタスク分解の粒度
- 意味なく詳細化しすぎたり細かく分解しすぎても逆に工数が増えるだけになってしまい、効果が落ちます。
- 自分や職場の中で品質と工数のバランスをとるのが大事だと思います。
- その意味で、目的に沿ったツールを自作できたのはよかったと思います。
- またタスクを適切に分割すると「完了したタスク」も見えてきて、これにより「達成感を得る」というプラス要素も発生します。(2025/02/28追記)
その日の依頼はその日の対応しない
- これはgtdとは関係ないのですが「すぐに着手しない」のがタスク管理のコツかもしれないと思っています。
- それよりもすでに着手しているタスクを目標のところまでこなしていった方が達成感を得られます。
- もちろん重要度や納期にもよりますが。
- 後から知りましたが「マニャーナの法則」として言われているようですね。「Do It Tomorrow」今日発生した仕事は明日やる(2025/02/28追記)
優先順位のつけ方
- もちろん基本的には指示の重要度や納期で優先度を決めますが、「時間管理のマトリクス」を考慮し第二領域を広げることを何より優先させています。
プレーンテキストである理由(2025/04/27追記)
- WYSIWYGのライブラリを拾ってきて搭載することはできるんだけどあえてそれは採用せず、プレーンテキスト+必要ならMarkdownを選択しています。
- プレーンテキストでないと以下のような問題/デメリットが出現します。
- 装飾が可能だと、カーソル移動操作やマウス操作のシーンが出現し効率が落ちる。
- 移植性が落ちる。
- 名著「達人プログラマー」にも「知識はプレーン・テキストに保存すること」が重要と説かれています。
- プレーンテキスト+Markdownにしておけばレンダリングエンジンがなくても目視で可読なのです。
WindowsREをベースにしたWindowsPEで無線LANアダプタのMACアドレス情報を取得する
目次

本エントリの概要
- WindowsPEで無線LANアダプタのMACアドレスを取得したいと思い、先人の記事を参考にWindowsREをベースにWindowsPEを作成した記録(ちらしのうら)です。
- この記事の対象は主に「WindowsPEを自分で作れる人」です。
前提
- 作成環境 : Windows11 24H2
背景
- WindowsPEでWindowsイメージのリカバリーなどをしているのですが、「リカバリー時にシリアル番号とMACアドレスを取得したい」という要求が発生しました。
- WindowsPEではドライバを入れても無線LANサービスが稼働せず、無線LANアダプタの情報を取得することができないと判明します。
- 先人が全く同じ内容で簡単な記録を残してくれていましたのでそれを参考に作ってみました。
用意するもの
- Windows ADK + WindowsPE作成環境
- Windows ADK のダウンロードとインストール | Microsoft Learn
- Windows ADK
- Windows ADK用の Windows PE アドオン
- インストール方法は省略します。(検索すればすぐに見つかると思います)
- Windows ADK のダウンロードとインストール | Microsoft Learn
- Windows11インストーラー
- 今回はMicrosoftのサイトからインストーラーのisoファイルをダウンロードしました。
- Windows 11 のダウンロード
- ネットワークデバイスのドライバ
- 対象デバイスのドライバをメーカーサイトなどからダウンロードし解凍しておきます。
- 主に**.infファイルがインストールに必要になります。
- startnet.cmd
- WindowsPE起動時のコマンドを変更したい場合は書き換えたものを用意して作業フォルダに置いておきます。
手順
1. WindowsREのwimの取り出し
- Windows11のインストーラーisoを右クリックからマウントします。→ A
- マウントしたAの
\sources\install.wimを作業フォルダにコピーします。 - install.wimをdismでマウントします。→ B
- マウントしたBの
\Windows\System32\Recovery\Winre.wimを作業フォルダにコピーします。 - Bを
/discardオプションでアンマウントします。 - Aを右クリック「取り出し」でアンマウントします。
コマンド例(3~5)
MD C:\work\winre dism /Mount-Image /ImageFile:"C:\work\resource\install.wim" /index:1 /MountDir:"C:\work\winre" copy C:\work\winre\Windows\System32\Recovery\Winre.wim C:\work\resource\ dism /Unmount-Image /mountdir:"C:\work\winre" /discard
2. WindowsREをベースにWindowsPEを作る
copypeコマンドでベースを作ります。- Winre.wimをboot.wimの名前で上書きします。
- boot.wimをマウントします。
- WindowsPEで必要なパッケージ群を追加します。
- ロケールやタイムゾーンを設定します。
- デバイスドライバを追加します。
- ドライバの上位階層を指定して
/recurseオプションをつければ自動的に探してくれます。
- ドライバの上位階層を指定して
- wlanサービスに必要なファイルを追加します。
- 以下2つのファイルをマウント先の同じディレクトリにコピーします。
C:\Windows\System32\dmcmnutils.dllC:\Windows\System32\mdmregistration.dll
- 以下2つのファイルをマウント先の同じディレクトリにコピーします。
\Windows\system32\winpeshl.iniをリネームします。- (必要なら)startnet.cmdをカスタムしたものに置き換えます。
/commitオプションでアンマウントします。
コマンド例
SET SOURCE_DIR=C:\work\resource SET WS_DIR=C:\work\WinPE_amd64 SET PACK_DIR=C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs CD /D C:\work copype amd64 %WS_DIR% rem boot.wimをWindowsREのイメージに置き換える ren %WS_DIR%\media\sources\boot.wim boot.wim.default copy %SOURCE_DIR%\windows\Winre.wim %WS_DIR%\media\sources\boot.wim rem マウントする dism /Mount-Image /ImageFile:"%WS_DIR%\media\sources\boot.wim" /index:1 /MountDir:"%WS_DIR%\mount" dism /Get-Packages /Image:"%WS_DIR%\mount" rem ここからパッケージ追加 rem ネットワーク WinPE-Dot3Svc dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\WinPE-Dot3Svc.cab" rem WMI WinPE-WMI: dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\WinPE-WMI.cab" dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\ja-jp\WinPE-WMI_ja-jp.cab" rem Microsoft .NET WinPE-NetFX: dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\WinPE-NetFx.cab" dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\ja-jp\WinPE-NetFx_ja-jp.cab" rem スクリプト WinPE-Scripting: dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\WinPE-Scripting.cab" dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\ja-jp\WinPE-Scripting_ja-jp.cab" rem PowerShell WinPE-PowerShell: dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\WinPE-PowerShell.cab" dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\ja-jp\WinPE-PowerShell_ja-jp.cab" rem PowerShell DISMコマンド WinPE-DismCmdlets: dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\WinPE-DismCmdlets.cab" dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\ja-jp\WinPE-DismCmdlets_ja-jp.cab" rem PowerShell SecureBoot WinPE-SecureBootCmdlets: dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\WinPE-SecureBootCmdlets.cab" rem SecureStartup WinPE-SecureStartup: dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\WinPE-SecureStartup.cab" dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\ja-jp\WinPE-SecureStartup_ja-jp.cab" rem HTA WinPE-HTA: dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\WinPE-HTA.cab" dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\ja-jp\WinPE-HTA_ja-jp.cab" rem 日本語言語パック: dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\ja-jp\lp.cab" rem 日本語フォントパッケージ: dism /Add-Package /Image:"%WS_DIR%\mount" /PackagePath:"%PACK_DIR%\WinPE-FontSupport-JA-JP.cab" rem ロケールを日本語に設定 rem 入力ロケールを日本語に設定 rem キーボードを日本語に設定 rem タイムゾーンを日本標準時に設定 dism /image:"%WS_DIR%\mount" /Set-AllIntl:ja-jp dism /image:"%WS_DIR%\mount" /Set-InputLocale:0411:00000411 dism /image:"%WS_DIR%\mount" /Set-LayeredDriver:6 dism /image:"%WS_DIR%\mount" /Set-TimeZone:"Tokyo Standard Time" rem ドライバインストール dism /Add-Driver /Image:"%WS_DIR%\mount" /Driver:%SOURCE_DIR%\drivers /Recurse rem wlanサービスに必要なファイルを追加する copy /Y C:\Windows\System32\dmcmnutils.dll %WS_DIR%\mount\Windows\system32\ copy /Y C:\Windows\System32\mdmregistration.dll %WS_DIR%\mount\Windows\system32\ rem winpeshl.iniをリネーム ren %WS_DIR%\mount\Windows\system32\winpeshl.ini winpeshl.ini.default rem 初期化コマンドに差し替える copy /Y %SOURCE_DIR%\startnet.cmd %WS_DIR%\mount\Windows\system32\ rem コミットしてディスクイメージをアンマウント dism /Unmount-Image /mountdir:"%WS_DIR%\mount" /commit
これで作ったイメージでWindowsPEとして起動すれば無線LANアダプタのデバイス情報も取得できるようになります。
3. 製造番号と無線LANアダプタの情報取得方法
PowerShellで取り出すコマンドサンプルです。
# Get CurrentTime $today = (get-date) $current = $today.toString() # Get SerialNumber $serial_lines = (wmic bios get serialnumber) # タイトル行と空行を省き、トリムする $serial_number = ($sn | Where-Object{$_ -ne "" -and $_ -notmatch "SerialNumber"}).trim() # Get MACAddress # 「Microsoft」で始まる名前は仮想アダプタなので省く $netadapters = (Get-CimInstance -class Win32_NetworkAdapterConfiguration | Where-Object{$_.Description -notmatch "Microsoft.*"} | Select-Object Description,MACAddress) # Output $info = "CurrentTime : " + $current + "`r`n" + "SerialNumber : " + $serial_number + "`r`n" + "MACAddress1 : " + $netadapters[0].MACAddress + " (" + $netadapters[0].Description + ")`r`n" + "MACAddress2 : " + $netadapters[1].MACAddress + " (" + $netadapters[1].Description + ")" $info
ポイント
- 無駄な操作もあるかもしれません。
- 参考にした記事ではstartnet.cmdに「net start wlansvc」を追記するよう書かれていましたが、その通りにして起動すると「すでに開始済」とのメッセージが出ました。バージョンによって異なるのかもしれません。
MySQL8.0で北極点と南極点2点間に線を引いてみた
目次

本エントリの概要
この記事は RDBMS-GIS(MySQL,PostgreSQLなど) Advent Calendar 2022 の6日目として作成しています。
ふと思いついてやってみたけどよくわからないことになったメモです。
前提
- 手元にあったMySQL8.0.17で検証しています。
- SRIDは4326を使います。
- SRID=4326の場合、
AXIS["Lat",NORTH],AXIS["Lon",EAST]の順番なので、緯度→経度の順番で書きます。 - 今回はざっくりと算出したいので細かい精度はこだわりません。
極点の表し方
まずは2点の場所について考えます。
極点に経度はないので仮に0とします。
SELECT ST_Distance_Sphere( ST_GeomFromText('POINT(90 0)',4326), ST_GeomFromText('POINT(-90 0)',4326) );
結果:20015114.352233686 (単位はm(メートル)です)
地球一周が4万km弱なのでだいたいあってますね。
ちなみに経度は-180~180の間で設定できますので、こんなことをいろいろ試してみましたが、距離を測るといずれも同じ結果でした。
SELECT ST_Distance_Sphere( ST_GeomFromText('POINT(90 180)',4326), ST_GeomFromText('POINT(-90 0)',4326) ); SELECT ST_Distance_Sphere( ST_GeomFromText('POINT(90 0)',4326), ST_GeomFromText('POINT(-90 180)',4326) );
ここまでは特に問題ありません。
2点間で線を引く
では、北極点と南極点を結ぶ線を描いてみます。
WNTはLineString(90 0,-90 0)とします。
この線の長さを測ってみます。
SELECT ST_Length(ST_GeomFromText('LineString(90 0,-90 0)',4326));
結果:20003917.356955886
ヨシ、約2万kmです。
2点間距離の結果と少し違いますが、まぁそういうものだろうということで差は一旦忘れます。
線はどこにひかれたのか
「さて、この線が地球上のどこにひかれているのでしょうか。」(これが本エントリの主題です)
北極点と南極点の最短ルートであること、いずれかの緯線に沿っていることは確信するところですが、経度はどこを通ってもここでの計算上は同じになるはずです。
「とりあえずまぁ本初子午線を通ってるんじゃまいか」という仮説を立ててST_Contains()で緯度経度0 0を通っているか確かめます。
SELECT ST_Contains( ST_GeomFromText('LineString(90 0,-90 0)',4326), ST_GeomFromText('Point(0 0)',4326) );
結果:1 (引数1に引数2が含まれている場合1、含まれない場合0)
ヨシ。やっぱり。
念のためほかの経度を通っていないかも確認します。
SELECT ST_Contains( ST_GeomFromText('LineString(90 0,-90 0)',4326), ST_GeomFromText('Point(0 0)',4326) ) AS Lon0, ST_Contains( ST_GeomFromText('LineString(90 0,-90 0)',4326), ST_GeomFromText('Point(0 90)',4326) ) AS Lon90, ST_Contains( ST_GeomFromText('LineString(90 0,-90 0)',4326), ST_GeomFromText('Point(0 180)',4326) ) AS Lon180, ST_Contains( ST_GeomFromText('LineString(90 0,-90 0)',4326), ST_GeomFromText('Point(0 -90)',4326) ) AS Lonm90;
結果:Lon0のみ1、それ以外は0
ヨシ。やっぱり経度0のみです。
さて、さっき「経度は-180~180の間で設定できます」って試したのをこちらでもやってみます。
SELECT ST_Length(ST_GeomFromText('LineString(90 180,-90 180)',4326));
結果:20003917.356955886
同じ長さです。
この場合も経度0を通っているのか確認してみます。
SELECT ST_Contains( ST_GeomFromText('LineString(90 180,-90 180)',4326), ST_GeomFromText('Point(0 0)',4326) );
結果:0
あれ、通っていません。
ということは経度180を指定しているのが効いているのでしょうか。
SELECT ST_Contains( ST_GeomFromText('LineString(90 180,-90 180)',4326), ST_GeomFromText('Point(0 180)',4326) );
結果:1
そのようです。
念のためほかの経度も確認します。
SELECT ST_Contains( ST_GeomFromText('LineString(90 180,-90 180)',4326), ST_GeomFromText('Point(0 0)',4326) ) AS Lon0, ST_Contains( ST_GeomFromText('LineString(90 180,-90 180)',4326), ST_GeomFromText('Point(0 90)',4326) ) AS Lon90, ST_Contains( ST_GeomFromText('LineString(90 180,-90 180)',4326), ST_GeomFromText('Point(0 180)',4326) ) AS Lon180, ST_Contains( ST_GeomFromText('LineString(90 180,-90 180)',4326), ST_GeomFromText('Point(0 -90)',4326) ) AS Lonm90;
結果:Lon180のみ1、それ以外は0
問題なさそうです。
ちょっといたずら
北極点と南極点で違う経度を指定して、まずは距離を測ります。
SELECT ST_Length(ST_GeomFromText('LineString(90 0,-90 180)',4326));
結果:20003917.35695591
今度は距離が目視できないレベルで変化しました。
が、約2万kmであることは変わらないです。
さてこれはどこを通っているのでしょうか。
SELECT ST_Contains( ST_GeomFromText('LineString(90 0,-90 180)',4326), ST_GeomFromText('Point(0 0)',4326) ) AS Lon0, ST_Contains( ST_GeomFromText('LineString(90 0,-90 180)',4326), ST_GeomFromText('Point(0 90)',4326) ) AS Lon90, ST_Contains( ST_GeomFromText('LineString(90 0,-90 180)',4326), ST_GeomFromText('Point(0 180)',4326) ) AS Lon180, ST_Contains( ST_GeomFromText('LineString(90 0,-90 180)',4326), ST_GeomFromText('Point(0 -90)',4326) ) AS Lonm90;
結果:Lon0とLon180が1、それ以外は0
え?あれ?いったいなにが起こっているの。
経度0と180を緯度を10度ずつずらしながら検査してゆきます。
SET @line1 = ST_GeomFromText('LineString(90 0,-90 180)',4326); SELECT ST_Contains(@line1, ST_GeomFromText('Point(80 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(70 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(60 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(50 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(40 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(30 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(20 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(10 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(0 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-10 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-20 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-30 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-40 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-50 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-60 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-70 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-80 0)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(80 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(70 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(60 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(50 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(40 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(30 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(20 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(10 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(0 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-10 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-20 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-30 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-40 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-50 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-60 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-70 180)',4326)) AND ST_Contains(@line1, ST_GeomFromText('Point(-80 180)',4326));
結果:1
一つでも0が混ざれば結果は0になるはずですので、すべて通っていることになります。
これはつまり「経度0と経度180の両方に線があるので地球1周分のはずが、長さは約2万kmである」ということになりますね。
なぞです。
まとめ
MySQL8.0でSRIDを指定しないでGEOMETRY列作った場合の挙動
目次
本エントリの概要
- この記事は RDBMS-GIS(MySQL,PostgreSQLなど) Advent Calendar 2019 の6日目として後追いで作成しています。
- Oracle Technology Cafe #6 の際に「MySQL8.0でSRIDを指定しないでGEOMETRY列を作った場合の挙動」をsakaikさんに聞かれ、記憶があいまいなまま答えてしまったので改めて調べた内容を書きます。(結果的にはだいたいあってたんですが)
- 以下はMySQL8.0.17で検証しています。
SRIDを指定した場合
そもそもSRIDを指定して列を作った場合は、そのSRIDを指定して行を挿入しないとエラーになります。これはsakaikさんも記事を書かれています通りです。
SRIDを指定しない場合
ではSRIDを指定しないで列を作るとどうなるでしょうか。
mysql> CREATE TABLE geom1 (
-> `id` int unsigned NOT NULL,
-> `geom` GEOMETRY NOT NULL,
-> PRIMARY KEY (`id`)
-> );
Query OK, 0 rows affected (0.05 sec)
はい作れます。
では複数のSRIDを指定してレコードを登録してみます。
mysql> INSERT INTO geom1 VALUES
-> (1, ST_GeomFromText('POINT(1 1)', 4612)),
-> (2, ST_GeomFromText('POINT(2 2)', 4612)),
-> (3, ST_GeomFromText('POINT(1 1)', 4326)),
-> (4, ST_GeomFromText('POINT(2 2)', 4326)),
-> (5, ST_GeomFromText('POINT(1 1)')),
-> (6, ST_GeomFromText('POINT(2 2)'));
Query OK, 6 rows affected (0.01 sec)
Records: 6 Duplicates: 0 Warnings: 0
すんなり6行入りました。
値を確認してみましょう。
mysql> SELECT
-> id,ST_SRID(geom)
-> FROM geom1;
+----+---------------+
| id | ST_SRID(geom) |
+----+---------------+
| 1 | 4612 |
| 2 | 4612 |
| 3 | 4326 |
| 4 | 4326 |
| 5 | 0 |
| 6 | 0 |
+----+---------------+
6 rows in set (0.00 sec)
それぞれのSRIDは保持されています。 また、SRIDを指定しなかった挿入ではSRIDが0になることもわかります。
ではレコード同士を比較してみましょう。 今回は2つの距離を測る「ST_Dintance()」を使って2点間の距離を測ります。
まずは同じSRID同士id=1,2の比較です。
mysql> SELECT ST_Distance(
-> (SELECT geom FROM geom1 WHERE id=1),
-> (SELECT geom FROM geom1 WHERE id=2)
-> ) AS DIST FROM DUAL;
+--------------------+
| DIST |
+--------------------+
| 156874.38594646144 |
+--------------------+
1 row in set (0.00 sec)
問題ありません。(単位はメートル)
同じように3,4 5,6を測ってみます。
mysql> SELECT ST_Distance(
-> (SELECT geom FROM geom1 WHERE id=3),
-> (SELECT geom FROM geom1 WHERE id=4)
-> ) AS DIST FROM DUAL;
+-------------------+
| DIST |
+-------------------+
| 156874.3859490455 |
+-------------------+
1 row in set (0.00 sec)
mysql> SELECT ST_Distance(
-> (SELECT geom FROM geom1 WHERE id=5),
-> (SELECT geom FROM geom1 WHERE id=6)
-> ) AS DIST FROM DUAL;
+--------------------+
| DIST |
+--------------------+
| 1.4142135623730951 |
+--------------------+
1 row in set (0.00 sec)
5,6はルート2そのままなので単純な平面の距離を出していることがわかります。
では違うSRID同士で距離を測ろうとするとどうなるでしょうか。
id=2,3の比較
mysql> SELECT ST_Distance(
-> (SELECT geom FROM geom1 WHERE id=2),
-> (SELECT geom FROM geom1 WHERE id=3)
-> ) AS DIST FROM DUAL;
ERROR 3033 (HY000): Binary geometry function st_distance given two geometries of different srids: 4612 and 4326, which should have been identical.
エラーです。「違うSRIDが引数に与えられてるよ」的なことを言われています。まぁ当然と言えば当然ですね。
念のためid=4,5の比較も見てみましたが、ほぼ同様です。
mysql> SELECT ST_Distance(
-> (SELECT geom FROM geom1 WHERE id=4),
-> (SELECT geom FROM geom1 WHERE id=5)
-> ) AS DIST FROM DUAL;
ERROR 3033 (HY000): Binary geometry function st_distance given two geometries of different srids: 4326 and 0, which should have been identical.
そもそもテーブルに入れずに比較した場合も同じ結果でした。
mysql> SELECT ST_Distance(
-> ST_GeomFromText('POINT(1 1)', 4612),
-> ST_GeomFromText('POINT(1 1)', 4326)
-> ) FROM DUAL;
ERROR 3033 (HY000): Binary geometry function st_distance given two geometries of different srids: 4612 and 4326, which should have been identical.
まとめと個人的見解
- テーブル定義でSRIDを指定しないとどんなSRIDのレコードも受け入れる
- 違うSRIDの値同士を比較しようとするとエラー3033になる
- プロダクトで使う場合はSRIDをしっかりと定義しておくのがドメイン設計として正しいでしょう
(おまけ)「SRID=0」を明示してテーブル定義した場合
テーブル定義でもデータ挿入時もSRIDを指定しなかった場合、「SRID=0」の値になることがわかりました。
ではテーブル定義時に「SRID=0」を指定した場合の扱いはどうなるでしょうか。試してみます。
mysql> CREATE TABLE geom0 (
-> `id` int unsigned NOT NULL,
-> `geom` GEOMETRY NOT NULL SRID 0,
-> PRIMARY KEY (`id`)
-> );
Query OK, 0 rows affected (0.05 sec)
はい、作成は可能でした。
値を入れてみましょう。 まずはSRID=0を指定して挿入
mysql> INSERT INTO geom0 VALUES (1, ST_GeomFromText('POINT(1 1)',0));
Query OK, 1 row affected (0.01 sec)
これは想定通りです。 ではSRID指定なしではどうなるでしょうか。
mysql> INSERT INTO geom0 VALUES (2, ST_GeomFromText('POINT(1 1)'));
Query OK, 1 row affected (0.02 sec)
問題ありませんね。関数の引数を省略した場合のデフォルト値が0となっていて、その値をチェックしているのではないかと思われます。 では違うSRIDで入れてみます。
mysql> INSERT INTO geom0 VALUES (3, ST_GeomFromText('POINT(1 1)',4612));
ERROR 3643 (HY000): The SRID of the geometry does not match the SRID of the column 'geom'. The SRID of the geometry is 4612, but the SRID of the column is 0. Consider changing the SRID of the geometry or the SRID property of the column.
ですよね。 つまり、「SRIDを指定しないで列を作った場合」と「SRID=0を指定して列を作った場合」は違うということになります。
(おまけ)存在しないSRIDの扱い
MySQLに定義されていないSRIDを使おうとするとどうなるでしょう。ここでは9999で試してみます。
テーブル定義しようとすると
mysql> CREATE TABLE geom9 (
-> `id` int unsigned NOT NULL,
-> `geom` GEOMETRY NOT NULL SRID 9999,
-> PRIMARY KEY (`id`)
-> );
ERROR 3548 (SR001): There's no spatial reference system with SRID 9999.
選択しようとすると
mysql> SELECT ST_GeomFromText('POINT(1 1)', 9999);
ERROR 3548 (SR001): There's no spatial reference system with SRID 9999.
同じエラーで門前払いでした。
MySQL5.5からMySQL8.0にマイグレーションしたゆるい話
目次
- 本エントリの概要
- 前提と環境条件
- 背景
- 要件
- 移行手順詳細
- 感想
- 参考) 非サポートな操作(のひとつ)
本エントリの概要
この記事は MySQL Advent Calendar 2019 - Qiita の15日目です。
タイトルの通り、職場のMySQL5.5を8.0にマイグレーションした話を書きます。