MySQL (взрывающийся / совпадающий массив) - PullRequest
4 голосов
/ 31 июля 2010

Вопрос1:

Таблица MySQL

id |массив
1 |1,2,3
2 |2
3 |2,3
4 |4,5,6

$_GET['id'] = 2;
$a = mysql_query("SELECT * FROM `table` WHERE `array` ??? '$_GET[id]'");

На этом шаге я хочу просмотреть массив whole и посмотреть, совпадает ли он с $ _GET ['id'],поэтому он должен вывести:

идентификаторы: 1,2,3

Вопрос2:

Таблица MySQL

id|массив
1 |4,5,6
2 |3,4,7

$_GET['id'] = 4;
$a = mysql_query("SELECT * FROM `table` WHERE `array` ??? '$_GET[id]'");

На этом шаге я только хочу сопоставить первый элемент в массиве, поэтому он должен вывести:

id: 4

Я могу думать только об использовании PHP для этого, но я бы предпочел сделать все это только в запросе MySQL, если это вообще возможно.

$a = mysql_query("SELECT * FROM `table`");
while($b = mysql_fetch_assoc($a))
{
    $elements = explode(',', $b['array']);
    foreach($elements as $element)
    {
        if($element == $_GET['id'])
        {
            echo $b['id'].'<br />';
        }
    }
}

или

$a = mysql_query("SELECT * FROM `table`");
while($b = mysql_fetch_assoc($a))
{
    $array = $b['array'];

    if(in_array($_GET['id'], $array))
    {
        echo $b['id'].'<br />';
    }
}

это выглядело бы просто ужасно.

Ответы [ 6 ]

4 голосов
/ 31 июля 2010

То, что вы можете / должны структурировать свою базу данных по-другому, уже упоминалось (см. http://en.wikipedia.org/wiki/Database_normalization). Но ....

См. FIND_IN_SET ()

mysql> SELECT FIND_IN_SET ('b', 'a, b, c, d');-> 2

например,

<?php
$mysql = init();    
bar($mysql, 1);
bar($mysql, 2);
bar($mysql, 3);
bar($mysql, 4);


function bar($mysql, $x) {
  $sql_x = mysql_real_escape_string($x, $mysql);
  $result = mysql_query("SELECT id, foo FROM soTest WHERE FIND_IN_SET('$sql_x', foo)", $mysql) or die(mysql_error());

  echo "$x:\n";
  while( false!==($row=mysql_fetch_array($result, MYSQL_ASSOC)) ) {
    echo $row['id'], ' ', $row['foo'], "\n";
  }
  echo "----\n";
}

function init() {
  $mysql = mysql_connect('localhost', 'localonly', 'localonly') or die(mysql_error());
  mysql_select_db('test', $mysql) or die(mysql_error());
  mysql_query('CREATE TEMPORARY TABLE soTest (id int auto_increment, foo varchar(64), primary key(id))', $mysql) or die(__LINE__.' '.mysql_error());
  mysql_query("INSERT INTO soTest (foo) VALUES ('1,2,3'), ('2,4'), ('3'), ('2,3'), ('1,2')", $mysql) or die(__LINE__.' '.mysql_error());
  return $mysql;
}

print

1:
1 1,2,3
5 1,2
----
2:
1 1,2,3
2 2,4
4 2,3
5 1,2
----
3:
1 1,2,3
3 3
4 2,3
----
4:
2 2,4
----

MySQL не может использовать индексы для выполнения этого поиска, то есть в результате запроса выполняется полное сканирование таблицы,см. Оптимизация запросов с помощью EXPLAIN


edit:
Для вашего второго вопроса вам нужно только изменить WHERE-предложение на
WHERE FIND_IN_SET('$sql_x', foo)=1

2 голосов
/ 31 июля 2010

Ваша структура данных в БД не оптимальна для запросов так, как вы этого хотите.

По первому вопросу:

mysql_query("SELECT * FROM table WHERE array LIKE '%,$_GET[id],%' OR array LIKE '$_GET[id],%' OR array LIKE '%,$_GET[id]' OR array = '$_GET[id]'");

Для второго:

mysql_query("SELECT id, SUBSTR(array, 1, POSITION(',' IN array) - 1) AS array FROM table WHERE array LIKE '$_GET[id],%' OR array = '$_GET[id]'");

Как видите, эти запросы не очень красивые, но они будут делать то, что вы хотите.

0 голосов
/ 31 июля 2010

Если вы не можете нормализовать свою схему (что является лучшим вариантом:

SELECT * 
  FROM table 
 WHERE ','+array+',' LIKE '%,$_GET[id],%' 

Но если вам нужен доступ к записям по id, то вам действительно следует нормализовать

0 голосов
/ 31 июля 2010

Я бы порекомендовал вам привести вашу базу данных в первую нормальную форму , e. г.

CREATE TABLE t_master (
    id INT PRIMARY KEY AUTO_INCREMENT
);

CREATE TABLE t_array (
    id INT PRIMARY KEY AUTO_INCREMENT,
    master_id INT NOT NULL,
    value INT,
    CONSTRAINT fk_array_master_id FOREIGN KEY (master_id) REFERENCES t_master (id)
);

Затем вы можете найти записи в t_master, которые имеют определенный value с

$q = 'SELECT m.* ' .
    'FROM t_master AS m INNER JOIN t_array AS a ON a.master_id = m.id ' .
    "WHERE a.value = '" . mysql_real_escape_string($_GET['id'], $db) . "' " .
    'GROUP BY m.id';

Наиболее важным преимуществом является то, что если у вас много значений, вы можете добавить индекс, чтобы найти их намного быстрее:

ALTER TABLE t_array ADD INDEX idx_value (value);

Менее очевидным, но не последним преимуществом является то, что ваши запросы становятся более логичными и структурированными.

0 голосов
/ 31 июля 2010

Первый:

SELECT * FROM table WHERE array LIKE '$_GET[id],%' OR array LIKE '%,$_GET[id],%' OR array LIKE '%,$_GET[id]' OR array = '$_GET[id]

Второй:

SELECT * FROM table WHERE array LIKE '$_GET[id],%' OR array = '$_GET[id]

Объяснение:

  • '$_GET[id],%' будет соответствовать, если массив начинается с $_GET[id]
  • '%,$_GET[id],%', если $_GET[id] находится между любыми двумя элементами массива
  • '%,$_GET[id]' будет соответствовать, есликонец массива равен $_GET[id]
  • array = '$_GET[id]', если массив содержит только один элемент, равный $_GET[id]
0 голосов
/ 31 июля 2010

Не проверено, но вы можете использовать:

Вопрос 1:

SELECT * FROM table WHERE array REGEXP '(^|,)?(,|$)';
// Match either the start of the string, or a , then the query value, then either a , or the end of the string

Вопрос 2:

SELECT * FROM table WHERE array REGEXP '^?(,|$)';
// Match the start of the string, then the query value, then either a , or the end of the string

Где ? заменяется наваше $_GET значение.Нет представления о производительности этого.

...