LoginSignup
8
8

More than 5 years have passed since last update.

mysqlで複数行を1行にまとめて1クエリで取得してphpで配列に戻す(フリーテキストを考慮エスケープあり)

Last updated at Posted at 2016-07-07

とりあえず簡易処理です。
例えば1記事に対してタグが複数つくようなシステムがあるとします。
記事テーブルてきなものに、記事タグテーブルてきなものが1対多になっている感じとします :exclamation:

posts (記事テーブル)

id title
1 夏休みのおすすめスポット

post_tags (記事に付与されたタグ)

post_id tag_id name
1 34 夏休み
1 567 おすすめ
1 98 子供

これを諸所の理由により、1クエリしかできない時に記事の1レコードに複数のタグも一緒にまとめて、1行で取得したい!

そんなときは、

SELECT
    p.id AS id,
    p.title AS title,
    t.ids AS tag_ids,
    t.names AS tag_names
FROM
    posts AS p
LEFT JOIN (
    SELECT
        pt.post_id,
        GROUP_CONCAT(pt.tag_id) AS ids,
        GROUP_CONCAT(REPLACE(REPLACE(pt.name, '&', '&'), ',', ',')) AS names
    FROM
        post_tags AS pt
    GROUP BY 
        pt.post_id
) AS t ON t.post_id = p.id

タグの複数行をGROUP_CONCATを使って1行にまとめます。
GROUP_CONCATで「34,567,98」のように、カンマ区切りで1行になってくれます。
これをphpで

$tagIds = explode(',', $row['tag_ids'])

のようにカンマ区切りで配列にもどせば完了!

しかし、ちょっとだけ注意!

注意したいのがこれがidで中身が数値とわかってる場合のみ。
フリーテキストであるタグの名前「夏休み」とかの場合を考慮しなければいけない。
なぜなら、タグの名前に
「カ,ン,マ,区,切,り,し,て,や,ん,よ」
みたいな意地悪なタグを付けられてしまうかも。

それを考慮すると

GROUP_CONCAT(REPLACE(REPLACE(pt.name, '&', '&'), ',', ','))

htmlのエスケープ処理をちょっとだけ借用
取得時に「,」を「,」に置換してエスケープします。
さらにタグに「,」を含めるいじわるもできるので、「&」を「&」に置換してエスケープします。

そしてphp側でエスケープを戻す処理を足して

function unescape ($str)
{
    return str_replace('&', '&', str_replace(',', ',', $str));
}
$tags = [];
$ids = explode(',', $row['tag_ids']);
$names = explode(',', $row['tag_naames']);
foreach ($ids as $index => $id) {
    $tags[] = [
        'id'   => (int)$ids[$index],
        'name' => unescape($names[$index]),
    ];
}

と、すれば1クエリの縛りでも1対多のタグデータもphp上で配列として取り出せます :thumbsup:
以上です!

8
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8