it-swarm.com.ru

Как я могу симулировать переменную массива в MySQL?

появляется MySQL не имеет переменных массива. Что я должен использовать вместо этого? 


Похоже, предлагаются две альтернативы: A скаляр типа набора и временные таблицы. Вопрос, с которым я связан, предполагает первый. Но стоит ли использовать их вместо переменных массива? В качестве альтернативы, если я пойду с наборами, какой будет основанная на множестве идиома, эквивалентная foreach

63
einpoklum

Ну, я использовал временные таблицы вместо переменных массива. Не самое лучшее решение, но оно работает.

Обратите внимание, что вам не нужно формально определять их поля, просто создайте их, используя SELECT:

CREATE TEMPORARY TABLE IF NOT EXISTS my_temp_table
SELECT first_name FROM people WHERE last_name = 'Smith';

(См. Также Создать временную таблицу из оператора select без использования Create Table .)

61
einpoklum

Вы можете достичь этого в MySQL, используя цикл WHILE:

SET @myArrayOfValue = '2,5,2,23,6,';

WHILE (LOCATE(',', @myArrayOfValue) > 0)
DO
    SET @value = ELT(1, @myArrayOfValue);
    SET @myArrayOfValue= SUBSTRING(@myArrayOfValue, LOCATE(',',@myArrayOfValue) + 1);

    INSERT INTO `EXEMPLE` VALUES(@value, 'hello');
END WHILE;

Правка: В качестве альтернативы вы можете сделать это с помощью UNION ALL:

INSERT INTO `EXEMPLE`
(
 `value`, `message`
)
(
 SELECT 2 AS `value`, 'hello' AS `message`
 UNION ALL
 SELECT 5 AS `value`, 'hello' AS `message`
 UNION ALL
 SELECT 2 AS `value`, 'hello' AS `message`
 UNION ALL
 ...
);
33
Omesh

Попробуйте использовать функцию FIND_IN_SET () MySql Например. 

SET @c = 'xxx,yyy,zzz';

SELECT * from countries 
WHERE FIND_IN_SET(countryname,@c);

Примечание. Вам не нужно устанавливать переменную SET в StoredProcedure, если вы передаете параметр со значениями CSV.

16
Himalaya Garg

Не знаю о массивах, но есть способ хранить разделенные запятыми списки в обычном столбце VARCHAR.

И когда вам нужно найти что-то в этом списке, вы можете использовать функцию FIND_IN_SET () .

13
wormhit

В настоящее время использование JSON массива было бы очевидным ответом.

Так как это старый, но все еще актуальный вопрос, я привел короткий пример…. JSON-функции доступны начиная с mySQL 5.7.x/MariaDB 10.2.3

Я предпочитаю это решение ELT (), потому что оно действительно больше похоже на массив, и этот «массив» можно повторно использовать в коде.

Но будьте осторожны: он (JSON) намного медленнее, чем использование временной таблицы. Это просто более удобно. имо.

Вот как использовать массив JSON:

SET @myjson = '["gmail.com","mail.ru","arcor.de","gmx.de","t-online.de",
                "web.de","googlemail.com","freenet.de","yahoo.de","gmx.net",
                "me.com","bluewin.ch","hotmail.com","hotmail.de","live.de",
                "icloud.com","hotmail.co.uk","yahoo.co.jp","yandex.ru"]';

SELECT JSON_LENGTH(@myjson);
-- result: 19

SELECT JSON_VALUE(@myjson, '$[0]');
-- result: gmail.com

И вот небольшой пример, чтобы показать, как это работает в функции/процедуре:

DELIMITER //
CREATE OR REPLACE FUNCTION example() RETURNS varchar(1000) DETERMINISTIC
BEGIN
  DECLARE _result varchar(1000) DEFAULT '';
  DECLARE _counter INT DEFAULT 0;
  DECLARE _value varchar(50);

  SET @myjson = '["gmail.com","mail.ru","arcor.de","gmx.de","t-online.de",
                "web.de","googlemail.com","freenet.de","yahoo.de","gmx.net",
                "me.com","bluewin.ch","hotmail.com","hotmail.de","live.de",
                "icloud.com","hotmail.co.uk","yahoo.co.jp","yandex.ru"]';

  WHILE _counter < JSON_LENGTH(@myjson) DO
    -- do whatever, e.g. add-up strings...
    SET _result = CONCAT(_result, _counter, '-', JSON_VALUE(@myjson, CONCAT('$[',_counter,']')), '#');

    SET _counter = _counter + 1;
  END WHILE;

  RETURN _result;
END //
DELIMITER ;

SELECT example();
6
SeparateReality

Возможно, создайте временную таблицу памяти со столбцами (ключ, значение), если вам нужны ассоциативные массивы. Наличие таблицы памяти - это самое близкое к наличию массивов в MySQL.

3
Pavle Lekic

Вот как я это сделал.

Сначала я создал функцию, которая проверяет, есть ли в списке значений, разделенных запятыми, значение Long/Integer/что угодно:

CREATE DEFINER = 'root'@'localhost' FUNCTION `is_id_in_ids`(
        `strIDs` VARCHAR(255),
        `_id` BIGINT
    )
    RETURNS BIT(1)
    NOT DETERMINISTIC
    CONTAINS SQL
    SQL SECURITY DEFINER
    COMMENT ''
BEGIN

  DECLARE strLen    INT DEFAULT 0;
  DECLARE subStrLen INT DEFAULT 0;
  DECLARE subs      VARCHAR(255);

  IF strIDs IS NULL THEN
    SET strIDs = '';
  END IF;

  do_this:
    LOOP
      SET strLen = LENGTH(strIDs);
      SET subs = SUBSTRING_INDEX(strIDs, ',', 1);

      if ( CAST(subs AS UNSIGNED) = _id ) THEN
        -- founded
        return(1);
      END IF;

      SET subStrLen = LENGTH(SUBSTRING_INDEX(strIDs, ',', 1));
      SET strIDs = MID(strIDs, subStrLen+2, strLen);

      IF strIDs = NULL or trim(strIds) = '' THEN
        LEAVE do_this;
      END IF;

  END LOOP do_this;

   -- not founded
  return(0);

END;

Так что теперь вы можете искать идентификатор в списке идентификаторов через запятую, например так:

select `is_id_in_ids`('1001,1002,1003',1002);

И вы можете использовать эту функцию внутри предложения WHERE, например так:

SELECT * FROM table1 WHERE `is_id_in_ids`('1001,1002,1003',table1_id);

Это был единственный способ передать параметр «массив» в ПРОЦЕДУРУ.

3
chuckedw
DELIMITER $$
CREATE DEFINER=`mysqldb`@`%` PROCEDURE `abc`()
BEGIN
  BEGIN 
    set @value :='11,2,3,1,'; 
    WHILE (LOCATE(',', @value) > 0) DO
      SET @V_DESIGNATION = SUBSTRING(@value,1, LOCATE(',',@value)-1); 
      SET @value = SUBSTRING(@value, LOCATE(',',@value) + 1); 
      select @V_DESIGNATION;
    END WHILE;
  END;
END$$
DELIMITER ;
3
Sagar Gangwal

Это прекрасно работает для списка значений:

SET @myArrayOfValue = '2,5,2,23,6,';

WHILE (LOCATE(',', @myArrayOfValue) > 0)
DO
SET @value = ELT(1, @myArrayOfValue);
    SET @STR = SUBSTRING(@myArrayOfValue, 1, LOCATE(',',@myArrayOfValue)-1);
    SET @myArrayOfValue = SUBSTRING(@myArrayOfValue, LOCATE(',', @myArrayOfValue) + 1);

    INSERT INTO `Demo` VALUES(@STR, 'hello');
END WHILE;
1
user2664904

Разве массивы не являются эффективными? Если вы просто перебираете значения, я думаю, что курсор на временной (или постоянной) таблице имеет больше смысла, чем поиск запятых, не так ли? Также чище. Найти "mysql DECLARE CURSOR".

Для произвольного доступа - временная таблица с первичным ключом с числовым индексом. К сожалению, самый быстрый доступ, который вы получите, это хеш-таблица, а не случайный доступ.

1
Amaigus

Обе версии, использующие наборы, у меня не работали (протестировано с MySQL 5.5). Функция ELT () возвращает весь набор. Учитывая, что оператор WHILE доступен только в контексте PROCEDURE, я добавил его в свое решение:

DROP PROCEDURE IF EXISTS __main__;

DELIMITER $
CREATE PROCEDURE __main__()
BEGIN
    SET @myArrayOfValue = '2,5,2,23,6,';

    WHILE (LOCATE(',', @myArrayOfValue) > 0)
    DO
        SET @value = LEFT(@myArrayOfValue, LOCATE(',',@myArrayOfValue) - 1);    
        SET @myArrayOfValue = SUBSTRING(@myArrayOfValue, LOCATE(',',@myArrayOfValue) + 1);
    END WHILE;
END;
$
DELIMITER ;

CALL __main__;

Если честно, я не думаю, что это хорошая практика. Даже если это действительно необходимо, это едва читаемо и довольно медленно. 

1
jmmeier

Я удивлен, что ни один из ответов не упоминает ELT/FIELD.

ELT/FIELD работает очень похоже на массив, особенно если у вас есть статические данные.

FIND_IN_SET также работает аналогично, но не имеет встроенной дополнительной функции , Но ее достаточно легко написать.

mysql> select elt(2,'AA','BB','CC');
+-----------------------+
| elt(2,'AA','BB','CC') |
+-----------------------+
| BB                    |
+-----------------------+
1 row in set (0.00 sec)

mysql> select field('BB','AA','BB','CC');
+----------------------------+
| field('BB','AA','BB','CC') |
+----------------------------+
|                          2 |
+----------------------------+
1 row in set (0.00 sec)

mysql> select find_in_set('CC','AA,BB,CC');
+------------------------------+
| find_in_set('BB','AA,BB,CC') |
+------------------------------+
|                            2 |
+------------------------------+
1 row in set (0.00 sec)

mysql>  SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('AA,BB,CC',',',2),',',-1);
+-----------------------------------------------------------+
| SUBSTRING_INDEX(SUBSTRING_INDEX('AA,BB,CC',',',2),',',-1) |
+-----------------------------------------------------------+
| BB                                                        |
+-----------------------------------------------------------+
1 row in set (0.01 sec)
0
Jongab

Я знаю, что это немного запоздалый ответ, но недавно мне пришлось решить аналогичную проблему и подумал, что это может быть полезно для других.

Фон

Рассмотрим таблицу ниже под названием «mytable»:

 Starting table

Проблема заключалась в том, чтобы сохранить только последние 3 записи и удалить все старые записи, у которых systemid = 1 (в таблице может быть много других записей с другими значениями systemid).

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

DELETE FROM mytable WHERE id IN (SELECT id FROM `mytable` WHERE systemid=1 ORDER BY id DESC LIMIT 3)

Однако это еще не поддерживается в MySQL, и если вы попробуете это, вы получите сообщение об ошибке типа 

...doesn't yet support 'LIMIT & IN/ALL/SOME subquery'

Таким образом, требуется обходной путь, при котором массив значений передается в селектор IN с помощью переменной. Однако, поскольку переменные должны быть единичными значениями, мне нужно имитировать массив. Хитрость заключается в том, чтобы создать массив в виде списка значений (строки), разделенных запятыми, и назначить его переменной следующим образом

SET @myvar := (SELECT GROUP_CONCAT(id SEPARATOR ',') AS myval FROM (SELECT * FROM `mytable` WHERE systemid=1 ORDER BY id DESC LIMIT 3 ) A GROUP BY A.systemid);

Результат, хранящийся в @myvar:

5,6,7

Затем селектор FIND_IN_SET используется для выберите из имитируемого массива

SELECT * FROM mytable WHERE FIND_IN_SET(id,@myvar);

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

SET @myvar := (SELECT GROUP_CONCAT(id SEPARATOR ',') AS myval FROM (SELECT * FROM `mytable` WHERE systemid=1 ORDER BY id DESC LIMIT 3 ) A GROUP BY A.systemid);
DELETE FROM mytable WHERE FIND_IN_SET(id,@myvar);

Я знаю, что это очень конкретный случай. Однако его можно изменить, чтобы он подходил практически к любому другому случаю, когда переменная должна хранить массив значений. 

Я надеюсь, что это помогает.

0
Clinton

Вдохновленный функцией ELT (порядковый номер, строка1, строка2, строка3,…), я думаю, что следующий пример работает как пример массива:

set @i := 1;
while @i <= 3
do
  insert into table(val) values (ELT(@i ,'val1','val2','val3'...));
set @i = @i + 1;
end while;

Надеюсь, это поможет.

0
edward

В версии MYSQL после 5.7.x вы можете использовать тип JSON для хранения массива. Вы можете получить значение массива по ключу через MYSQL.

0
nick darn