나타납니다 MySQL이 배열 변수를 가지고 있지 않습니다. 대신 무엇을 사용해야합니까?
제안 된 두 가지 대안이있는 것 같습니다 : 집합 유형 스칼라 및 임시 테이블 . 내가 연결 한 질문은 전자를 제안합니다. 그러나 배열 변수 대신 이것을 사용하는 것이 좋은 습관입니까? 또는 세트로 가면 세트 기반 관용구는 foreach
무엇입니까?
답변:
글쎄, 나는 배열 변수 대신 임시 테이블을 사용하고 있습니다. 최고의 솔루션은 아니지만 작동합니다.
공식적으로 필드를 정의 할 필요는 없으며 SELECT를 사용하여 생성하기 만하면됩니다.
DROP TEMPORARY TABLE IF EXISTS my_temp_table;
CREATE TEMPORARY TABLE my_temp_table
SELECT first_name FROM people WHERE last_name = 'Smith';
( Create Table을 사용하지 않고 select 문에서 임시 테이블 만들기 도 참조하십시오 .)
WHILE
루프를 사용하여 MySQL에서이를 수행 할 수 있습니다 .
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
...
);
CALL
.
UNION ALL
을 사용 하거나 절차를 사용하지 않고 쉽게 할 수 있습니다 .
MySql의 FIND_IN_SET () 함수를 사용해보십시오.
SET @c = 'xxx,yyy,zzz';
SELECT * from countries
WHERE FIND_IN_SET(countryname,@c);
참고 : CSV 값과 함께 매개 변수를 전달하는 경우 StoredProcedure에서 변수를 설정할 필요가 없습니다.
요즘에는 JSON 배열을 사용하는 것이 분명한 대답이 될 것입니다.
이것은 오래되었지만 여전히 관련이있는 질문이기 때문에 짧은 예를 만들었습니다. mySQL 5.7.x / MariaDB 10.2.3부터 JSON 함수를 사용할 수 있습니다.
ELT ()보다이 솔루션을 선호하는 이유는 배열과 비슷하고 코드에서이 '배열'을 재사용 할 수 있기 때문입니다.
하지만 조심하세요. (JSON)은 임시 테이블을 사용하는 것보다 확실히 훨씬 느립니다. 더 편리합니다. imo.
다음은 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();
배열에 대해서는 모르지만 일반 VARCHAR 열에 쉼표로 구분 된 목록을 저장하는 방법이 있습니다.
그리고 그 목록에서 무언가를 찾아야 할 때 FIND_IN_SET () 함수를 사용할 수 있습니다 .
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 ;
답변이 다소 늦었다는 것을 알고 있지만 최근에 비슷한 문제를 해결해야했고 이것이 다른 사람에게 유용 할 것이라고 생각했습니다.
배경
'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);
나는 이것이 매우 특정한 경우라는 것을 알고 있습니다. 그러나 변수가 값의 배열을 저장해야하는 다른 경우에 맞게 수정할 수 있습니다.
이것이 도움이되기를 바랍니다.
연관 배열을 원하는 경우 열 (키, 값)이있는 임시 메모리 테이블을 만들 수 있습니다. 메모리 테이블을 갖는 것은 mysql에 배열을 갖는 것과 가장 가까운 것입니다.
내가 한 방법은 다음과 같습니다.
먼저 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;
이제 다음과 같이 쉼표로 구분 된 ID 목록에서 ID를 검색 할 수 있습니다.
select `is_id_in_ids`('1001,1002,1003',1002);
그리고 다음과 같이 WHERE 절 내에서이 함수를 사용할 수 있습니다.
SELECT * FROM table1 WHERE `is_id_in_ids`('1001,1002,1003',table1_id);
이것이 PROCEDURE에 "array"매개 변수를 전달하는 유일한 방법이었습니다.
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('BB','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)
이것은 값 목록에 대해 잘 작동합니다.
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 문은 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__;
솔직히 말해서 이것이 좋은 습관이라고 생각하지 않습니다. 실제로 필요한 경우에도 거의 읽을 수없고 매우 느립니다.
같은 문제를 보는 또 다른 방법. 도움이되는 희망
DELIMITER $$
CREATE PROCEDURE ARR(v_value VARCHAR(100))
BEGIN
DECLARE v_tam VARCHAR(100);
DECLARE v_pos VARCHAR(100);
CREATE TEMPORARY TABLE IF NOT EXISTS split (split VARCHAR(50));
SET v_tam = (SELECT (LENGTH(v_value) - LENGTH(REPLACE(v_value,',',''))));
SET v_pos = 1;
WHILE (v_tam >= v_pos)
DO
INSERT INTO split
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(v_value,',',v_pos),',', -1);
SET v_pos = v_pos + 1;
END WHILE;
SELECT * FROM split;
DROP TEMPORARY TABLE split;
END$$
CALL ARR('1006212,1006404,1003404,1006505,444,');
다음은 쉼표로 구분 된 문자열을 반복하는 MySQL의 예입니다.
DECLARE v_delimited_string_access_index INT;
DECLARE v_delimited_string_access_value VARCHAR(255);
DECLARE v_can_still_find_values_in_delimited_string BOOLEAN;
SET v_can_still_find_values_in_delimited_string = true;
SET v_delimited_string_access_index = 0;
WHILE (v_can_still_find_values_in_delimited_string) DO
SET v_delimited_string_access_value = get_from_delimiter_split_string(in_array, ',', v_delimited_string_access_index); -- get value from string
SET v_delimited_string_access_index = v_delimited_string_access_index + 1;
IF (v_delimited_string_access_value = '') THEN
SET v_can_still_find_values_in_delimited_string = false; -- no value at this index, stop looping
ELSE
-- DO WHAT YOU WANT WITH v_delimited_string_access_value HERE
END IF;
END WHILE;
get_from_delimiter_split_string
여기에 정의 된 함수를 사용합니다 : https://stackoverflow.com/a/59666211/3068233
원래 여기에 MySQL 테이블 변수로 배열을 추가하고 싶었 기 때문에 묻습니다. 저는 데이터베이스 디자인에 비교적 익숙하지 않았고 일반적인 프로그래밍 언어 방식으로 어떻게 할 것인지 생각하려고했습니다.
그러나 데이터베이스는 다릅니다. 나는 생각했다 나는 변수로 배열을 싶었지만, 그냥 일반적인 MySQL 데이터베이스 연습이 아니다 것으로 나타났다.
배열에 대한 대체 솔루션은 추가 테이블을 추가 한 다음 외래 키로 원본 테이블을 참조하는 것입니다.
예를 들어, 가정의 모든 사람이 상점에서 구매하고자하는 모든 항목을 추적하는 애플리케이션을 상상해 봅시다.
원래 구상했던 테이블을 만드는 명령은 다음과 같을 것입니다.
#doesn't work
CREATE TABLE Person(
name VARCHAR(50) PRIMARY KEY
buy_list ARRAY
);
나는 buy_list가 쉼표로 구분 된 항목의 문자열 또는 이와 유사한 것으로 구상했다고 생각합니다.
그러나 MySQL에는 배열 유형 필드가 없으므로 다음과 같은 것이 정말로 필요했습니다.
CREATE TABLE Person(
name VARCHAR(50) PRIMARY KEY
);
CREATE TABLE BuyList(
person VARCHAR(50),
item VARCHAR(50),
PRIMARY KEY (person, item),
CONSTRAINT fk_person FOREIGN KEY (person) REFERENCES Person(name)
);
여기에서 fk_person이라는 제약 조건을 정의합니다. BuyList의 'person'필드가 외래 키라고 말합니다. 즉, 다른 테이블의 기본 키, 특히 REFERENCES가 나타내는 Person 테이블의 'name'필드입니다.
또한 사람과 항목의 조합을 기본 키로 정의했지만 기술적으로는 필요하지 않습니다.
마지막으로 사람의 목록에있는 모든 항목을 가져 오려면 다음 쿼리를 실행할 수 있습니다.
SELECT item FROM BuyList WHERE person='John';
이것은 John의 목록에있는 모든 항목을 제공합니다. 배열이 필요하지 않습니다!
그런 테이블이 하나 있으면
mysql> select * from user_mail;
+------------+-------+
| email | user |
+------------+-------+-
| email1@gmail | 1 |
| email2@gmail | 2 |
+------------+-------+--------+------------+
및 배열 테이블 :
mysql> select * from user_mail_array;
+------------+-------+-------------+
| email | user | preferences |
+------------+-------+-------------+
| email1@gmail | 1 | 1 |
| email1@gmail | 1 | 2 |
| email1@gmail | 1 | 3 |
| email1@gmail | 1 | 4 |
| email2@gmail | 2 | 5 |
| email2@gmail | 2 | 6 |
CONCAT 함수를 사용하여 두 번째 테이블의 행을 하나의 배열로 선택할 수 있습니다.
mysql> SELECT t1.*, GROUP_CONCAT(t2.preferences) AS preferences
FROM user_mail t1,user_mail_array t2
where t1.email=t2.email and t1.user=t2.user
GROUP BY t1.email,t1.user;
+------------+-------+--------+------------+-------------+
| email | user | preferences |
+------------+-------+--------+------------+-------------+
|email1@gmail | 1 | 1,3,2,4 |
|email2@gmail | 2 | 5,6 |
+------------+-------+--------+------------+-------------+
이 답변을 개선 할 수 있다고 생각합니다. 이 시도:
'장난'매개 변수는 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 값 문자열을 루프가 반복 될 때마다 점점 더 짧아 지므로 최적화에 더 좋을 것이라고 생각합니다.
여러 컬렉션에 대해 이와 같은 것을 시도합니다. 저는 MySQL 초보자입니다. 함수 이름에 대해 죄송합니다. 어떤 이름이 가장 좋을지 결정할 수 없습니다.
delimiter //
drop procedure init_
//
create procedure init_()
begin
CREATE TEMPORARY TABLE if not exists
val_store(
realm varchar(30)
, id varchar(30)
, val varchar(255)
, primary key ( realm , id )
);
end;
//
drop function if exists get_
//
create function get_( p_realm varchar(30) , p_id varchar(30) )
returns varchar(255)
reads sql data
begin
declare ret_val varchar(255);
declare continue handler for 1146 set ret_val = null;
select val into ret_val from val_store where id = p_id;
return ret_val;
end;
//
drop procedure if exists set_
//
create procedure set_( p_realm varchar(30) , p_id varchar(30) , p_val varchar(255) )
begin
call init_();
insert into val_store (realm,id,val) values (p_realm , p_id , p_val) on duplicate key update val = p_val;
end;
//
drop procedure if exists remove_
//
create procedure remove_( p_realm varchar(30) , p_id varchar(30) )
begin
call init_();
delete from val_store where realm = p_realm and id = p_id;
end;
//
drop procedure if exists erase_
//
create procedure erase_( p_realm varchar(30) )
begin
call init_();
delete from val_store where realm = p_realm;
end;
//
call set_('my_array_table_name','my_key','my_value');
select get_('my_array_table_name','my_key');
데이터를 배열이나 한 행으로 저장하는 대신 수신 된 모든 값에 대해 다른 행을 만들어야합니다. 이렇게하면 모두 합치는 것보다 이해하기가 훨씬 간단 해집니다.
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'
);