Как я могу имитировать переменную массива в MySQL?
похоже, что MySQL не имеет переменных массива. Что я должен использовать вместо этого?
там, кажется, есть две альтернативы предложил: a set-type scalar и временные таблицы. Вопрос, с которым я связался, предполагает первое. Но это хорошая практика, чтобы использовать их вместо переменных массива? В качестве альтернативы, если я иду с наборами, то какой будет идиома на основе набора, эквивалентная foreach?
16 ответов:
Ну, я использую временные таблицы вместо переменных массива. Не самое лучшее решение, но оно работает.
обратите внимание, что вам не нужно формально определять их поля, просто создайте их с помощью SELECT:
CREATE TEMPORARY TABLE IF NOT EXISTS my_temp_table SELECT first_name FROM people WHERE last_name = 'Smith';(см. Также создать временную таблицу из инструкции select без использования Create Table.)
вы можете достичь этого в 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 ... );
попробуйте использовать функцию Find_in_set () MySql например,
SET @c = 'xxx,yyy,zzz'; SELECT * from countries WHERE FIND_IN_SET(countryname,@c);Примечание: вам не нужно устанавливать переменную в StoredProcedure, если вы передаете параметр со значениями CSV.
Не знаю о массивах, но есть способ хранить разделенные запятыми списки в обычном столбце VARCHAR.
и когда вам нужно найти что-то в этом списке, вы можете использовать FIND_IN_SET ()
может быть, создать временную таблицу памяти со столбцами (ключ, значение), если вы хотите ассоциативные массивы. Наличие таблицы памяти-это самое близкое к наличию массивов в mysql
вот как я это сделал.
во-первых, я создал функцию, которая проверяет, является ли длинное/целое/любое значение в списке значений, разделенных запятыми:
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);Это был единственный способ, которым я нашел, чтобы передать параметр "array" в процедуру.
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 ;
в настоящее время с помощью массив JSON был бы очевидный ответ.
поскольку это старый, но все еще актуальный вопрос, я привел короткий пример. Функции JSON доступны начиная с mySQL 5.7.x / MariaDB 10.2.3
Я предпочитаю это решение за ЭЛТ (), потому что это действительно больше похоже на массив, и этот массив может быть использован в коде.
но будьте осторожны: это (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();
Это прекрасно работает для списка значений:
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;
обе версии с использованием наборов не работали для меня (протестировано с MySQL 5.5). Функция ELT () возвращает весь набор. Учитывая, что оператор WHILE доступен только в контексте процедуры, я добавил его в свое решение:
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__;честно говоря, я не думаю, что это хорошая практика. Даже если это действительно необходимо, это едва читается и довольно медленно.
разве точка массивов не должна быть эффективной? Если вы просто перебираете значения, я думаю, что курсор на временной (или постоянной) таблице имеет больше смысла, чем поиск запятых, нет? Также чище. Поиск "mysql объявить курсор".
для произвольного доступа временная таблица с численно индексированным первичным ключом. К сожалению, самый быстрый доступ, который вы получите, - это хэш-таблица, а не истинный случайный доступ.
в версии MYSQL после 5.7.x, вы можете использовать тип JSON для хранения массива. Вы можете получить значение массива ключом через MYSQL.
вдохновленный функцией ELT(index number, string1, string2, string3,...), я думаю, что следующий пример работает как пример массива:
set @i := 1; while @i <= 3 do insert into table(val) values (ELT(@i ,'val1','val2','val3'...)); set @i = @i + 1; end while;надеюсь, что это поможет.
Я знаю, что это немного поздний ответ, но недавно мне пришлось решить аналогичную проблему и подумал, что это может быть полезно для других.
фон
Рассмотрим таблицу ниже под названием "mytable":
проблема заключалась в том, чтобы сохранить только последние 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);Я знаю, что это очень специфический случай. Однако он может быть изменен, чтобы удовлетворить почти любой другой случай где переменная должна хранить массив значений.
Я надеюсь, что это помогает.
Я думаю, что могу улучшить этот ответ. Попробуйте это:
параметр 'шалости' является CSV. то есть. - 1,2,3,4.....и т. д.
CREATE PROCEDURE AddRanks( IN Pranks TEXT ) BEGIN DECLARE VCounter INTEGER; DECLARE VStringToAdd VARCHAR(50); SET VCounter = 0; START TRANSACTION; REPEAT SET VStringToAdd = (SELECT TRIM(SUBSTRING_INDEX(Pranks, ',', 1))); SET Pranks = (SELECT RIGHT(Pranks, TRIM(LENGTH(Pranks) - LENGTH(SUBSTRING_INDEX(Pranks, ',', 1))-1))); INSERT INTO tbl_rank_names(rank) VALUES(VStringToAdd); SET VCounter = VCounter + 1; UNTIL (Pranks = '') END REPEAT; SELECT VCounter AS 'Records added'; COMMIT; END;этот метод делает искомую строку значений CSV постепенно короче с каждой итерацией цикла, что, по моему мнению, было бы лучше для оптимизации.
вы пробовали использовать PHP serialize ()? Это позволяет хранить содержимое массива переменной в строке, которую PHP понимает и безопасно для базы данных (при условии, что вы сначала ее экранировали).
$array = array( 1 => 'some data', 2 => 'some more' ); //Assuming you're already connected to the database $sql = sprintf("INSERT INTO `yourTable` (`rowID`, `rowContent`) VALUES (NULL, '%s')" , serialize(mysql_real_escape_string($array, $dbConnection))); mysql_query($sql, $dbConnection) or die(mysql_error());вы также можете сделать то же самое без нумерованного массива
$array2 = array( 'something' => 'something else' );или
$array3 = array( 'somethingNew' );

Comments