質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

MariaDB

MariaDBは、MySQL派生のオープンソースなリレーショナルデータベースシステムです。 また、MySQLとほぼ同じデータベースエンジンに対応しています。

Q&A

解決済

2回答

6649閲覧

INの値が重複した時、複数行取得したい

aglkjggg

総合スコア769

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

MariaDB

MariaDBは、MySQL派生のオープンソースなリレーショナルデータベースシステムです。 また、MySQLとほぼ同じデータベースエンジンに対応しています。

0グッド

0クリップ

投稿2016/11/23 10:45

編集2016/11/23 11:08

accounts テーブルは表1のようになっています。

表1. accountsテーブル

idname
1大塚
2山田
3斎藤
4杉村
5武田

select * from accounts where id in(1, 2)
とすると表2の情報が取得できます。

表2

idname
1大塚
2山田

select * from accounts where id in(1, 1, 2)
とすると表3のような情報を取得したいのですが、
実行すると表2の結果となります。
重複分は消されてしまいます。

表3

idname
1大塚
1大塚
2山田

環境

MariaDB 15.1
PHP 5.6.24

質問

どのようにすればINで指定した数だけ、重複ありで取得できますでしょうか?

アプリケーションで実装すると、汚くなるので出来る限りSQLだけで完結させたいです。

PHP

1$accountIds = array(1, 1, 2); 2$results = $db->query('select id, name from accounts where id in('. join(',', $accountIds). ')'); 3 4$accountIdAndNameList = array(); 5 6foreach($accountIds as $accountId) 7{ 8 foreach($results as $x) 9 { 10 if($accountId == $x->id) 11 { 12 $data = new stdClass(); 13 $data->id = $x->id; 14 $data->name = $x->name; 15 16 array_push($accountIdAndNameList, $data); 17 } 18 } 19}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KiyoshiMotoki

2016/11/23 11:02

お使いのDB製品の名称(例えば MySQL や PostgerSQL など)を、タグまたは質問欄に追記してください。それによって、解決方法が変わる可能性がありますので。
guest

回答2

0

とうの昔にクローズしているQAですが、どうしてもDB側の世界で解決することを強いられた場合のご参考に回答しておきます。

とはいっても今回のようなケースは、
どちらかというとレアケースと考えていますが。

#MySQL系以外の主要なDBMSの場合
例えばOracle、SQL Server、PostgreSQLではユーザ定義関数(以下UDFと書きます)の戻り値でテーブル型を返却できます。
厳密にいうとOracleはユーザ独自定義したTYPE型の返却なので、返却するテーブル型と同構造のTYPE型を作成して戻り値に指定する必要があります。

少し横にそれましたが、
それを用いるとFROM句に関数を記載でき、さもテーブルのように扱えるのです。

それがどうしたの?、
となるかもしれませんが、
これができるということはJOINも当然できますよねというお話になる訳です。

IN句に与えるカンマ区切りパラメータを引数に与えて、
その条件をテーブル型として返却するUDFを作れば以下のようなことができちゃう訳です。

SQL

1SELECT 2 a.* 3FROM 4 accounts a 5 -- INNER JOIN TABLE(make_cond_tbl('1, 1, 2')) c ← Oracle風 6 INNER JOIN make_cond_tbl('1, 1, 2') c 7 ON a.id = c.cond

作成したUDFが素直に3行検索値を返しさえすれば、
恐らく要望通りの動作をしてくれるなどはないでしょうか。

#MySQLの場合
恐らくMariaDBもそうなんだろうなと踏んでいますが、
残念なことに今の所はUDFでテーブル型を戻す手段はないようです。
(調べた限り、STRING|INTEGER|REAL|DECIMAL系の戻り値しか不可のようです。)

では上記のようなアプローチは不可能か、
というと不可能ではなく回りくどい方法になりますがストアドプロシージャ(PROCEDURE)を用いると似たようなことは可能なようです。

英語ですが、こちらが参考になるでしょう。

今回のQAでいくと以下のようなイメージでしょうか。

SQL

1CALL make_cond_tbl('1, 1, 2'); 2SELECT 3 a.* 4FROM 5 accounts a 6 INNER JOIN cond_tbl c 7 ON a.id = c.cond

ただし上記の手法をとると、
検索条件を実際に格納するテーブルが必要となるのでご注意ください。


恐らく課題はとっくの昔に解決してるとはずなので、
こういったアプローチもあるよ、というご参考程度に。

投稿2016/12/23 07:30

編集2016/12/23 07:33
Panzer_vor

総合スコア1636

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Panzer_vor

2016/12/23 07:54

補足ですが、 PostgreSQLは「REGEXP_SPLIT_TO_TABLE」関数で出来るので自作は不要です。
guest

0

ベストアンサー

情報の追記、ありがとうございます。

SQLだけで完結

しようとすると、少なくとも私には、以下のようにUNION ALLを使用する方法しか思いつきません。。

sql

1SELECT * FROM accounts WHERE id = 1 2UNION ALL 3SELECT * FROM accounts WHERE id = 1 4UNION ALL 5SELECT * FROM accounts WHERE id = 2;

sql

1mysql> CREATE TABLE accounts ( 2 -> id int PRIMARY KEY AUTO_INCREMENT, 3 -> name varchar(16) 4 -> ) DEFAULT CHARSET=UTF8; 5Query OK, 0 rows affected (0.04 sec) 6 7mysql> INSERT INTO accounts (name) VALUES 8 -> ('大塚'), 9 -> ('山田'), 10 -> ('斎藤'), 11 -> ('杉村'), 12 -> ('武田'); 13Query OK, 5 rows affected (0.01 sec) 14Records: 5 Duplicates: 0 Warnings: 0 15 16mysql> SELECT * FROM accounts; 17+----+--------+ 18| id | name | 19+----+--------+ 20| 1 | 大塚 | 21| 2 | 山田 | 22| 3 | 斎藤 | 23| 4 | 杉村 | 24| 5 | 武田 | 25+----+--------+ 265 rows in set (0.00 sec) 27 28mysql> SELECT * FROM accounts WHERE id = 1 29 -> UNION ALL 30 -> SELECT * FROM accounts WHERE id = 1 31 -> UNION ALL 32 -> SELECT * FROM accounts WHERE id = 2; 33+----+--------+ 34| id | name | 35+----+--------+ 36| 1 | 大塚 | 37| 1 | 大塚 | 38| 2 | 山田 | 39+----+--------+ 403 rows in set (0.00 sec)

もっとも、「SQLだけで完結」することにこだわらなければ、
PHPコードを以下のように実装すれば、同じ結果を取得できます。
(手元に MariaDB の環境が無いため、MySQL5.7 で動作確認)

php

1<?php 2$db = new PDO(...); 3 4$accountIds = array(1, 1, 2); 5$stmt = $db->prepare('select id, name from accounts where id = ?'); 6 7$accountIdAndNameList = array(); 8 9foreach($accountIds as $accountId) 10{ 11 $stmt->bindValue(1, $accountId, PDO::PARAM_INT); 12 $stmt->execute(); 13 14 while (($data = $stmt->fetch(PDO::FETCH_OBJ)) !== false) { 15 $accountIdAndNameList[] = $data; 16 } 17} 18 19var_dump($accountIdAndNameList);

実行結果

array(3) { [0]=> object(stdClass)#3 (2) { ["id"]=> string(1) "1" ["name"]=> string(4) "大塚" } [1]=> object(stdClass)#4 (2) { ["id"]=> string(1) "1" ["name"]=> string(4) "大塚" } [2]=> object(stdClass)#5 (2) { ["id"]=> string(1) "2" ["name"]=> string(4) "山田" } }

個人的には、それほど「汚い」コードとは思いませんが、いかがでしょうか?

投稿2016/11/23 11:30

KiyoshiMotoki

総合スコア4791

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aglkjggg

2016/11/23 11:45 編集

ご回答ありがとうございます。 foreachがネストせず、ifの比較がないので書いていただいたPHPのほうが可読性は高いですが、 UNION ALLや、書いていただいたPHPのプログラムだと SELECTがidの数だけ走ってしまうのでコストが高そうですね… 単純にレコードを取得するだけなので、 私の知らないSQLの句をSQLに加えるだけで簡単に複数取得でき、プログラムが短くなるかも… という思いで質問しましたが、現状が一番ベターな気がしてきました。。。 テーブルの作成やPHPコードで検証等していただきありがとうございました。 簡単にすることはできず、「こういうもの」という理解をすることが出来ました。 ありがとうございます。
KiyoshiMotoki

2016/11/23 12:19

aglkjggg様 レスありがとうございます。 > UNION ALLや、書いていただいたPHPのプログラムだと > SELECTがidの数だけ走ってしまうのでコストが高そうですね… 検索する id の個数や accountテーブルのレコード数にもよりますが、 idカラムにインデックスが張ってあれば、気にするほどのコストにはならないと思いますよ。 加えて、"PHPのプログラム"の方は、  $stmt = $db->prepare('select id, name from accounts where id = ?'); の前に以下の一文を実行しておくと、SQL文の解析が1回だけで済むので、さらに性能が改善できるかも知れません。  $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); http://php.net/manual/ja/pdo.setattribute.php > PDO::ATTR_EMULATE_PREPARES プリペアドステートメントのエミュレーションを有効または無効にする。 もっとも、MariaDB が PDO からのプリペアドステートメントの使用をサポートしているかはよく分からないので、 断言はできませんが。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問