Jak mogę symulować zmienną array w MySQL?

Wydaje się , że MySQL nie ma zmiennych tablicowych. Czego powinienem użyć zamiast tego?


Sugerowane są dwie alternatywy: Skalar typu set itabele tymczasowe . Pytanie, które podlinkowałem, sugeruje to pierwsze. Ale czy jest to dobra praktyka, aby używać tych zmiennych zamiast tablic? Alternatywnie, jeśli pójdę z zestawami, jaki byłby idiom oparty na zestawach równoważny foreach?

Author: Community, 2012-08-29

16 answers

Cóż, używam tabel tymczasowych zamiast zmiennych tablicowych. Nie najlepsze rozwiązanie, ale działa.

Zauważ, że nie musisz formalnie definiować ich pól, po prostu utwórz je za pomocą SELECT:

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

(Patrz również Tworzenie tymczasowej tabeli z instrukcji select bez użycia Create Table .)

 56
Author: einpoklum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:15

Możesz to osiągnąć w MySQL używając pętli 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;

Edytuj: Alternatywnie możesz to zrobić używając 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
 ...
);
 32
Author: Omesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-21 08:08:27

Spróbuj użyć funkcji FIND_IN_SET () MySql np.

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

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

Uwaga: nie musisz ustawiać zmiennej w StoredProcedure, jeśli przekazujesz parametr z wartościami CSV.

 13
Author: Himalaya Garg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-14 09:15:54

Nie wiem o tablicach, ale istnieje sposób na przechowywanie list rozdzielonych przecinkami w zwykłej kolumnie VARCHAR.

I kiedy chcesz znaleźć coś na tej liście, możesz użyć funkcji FIND_IN_SET () .

 11
Author: wormhit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-26 21:45:38

Może utworzyć tymczasową tabelę pamięci z kolumnami (klucz, wartość), jeśli chcesz tablic asocjacyjnych. Posiadanie tablicy pamięci jest najbliższą rzeczą do posiadania tablic w mysql

 3
Author: Pavle Lekic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-01 08:05:52

Oto Jak to zrobiłem.

Najpierw stworzyłem funkcję, która sprawdza, czy Long / Integer / whatever value znajduje się na liście wartości oddzielonych przecinkami:

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;

Więc teraz możesz wyszukać ID na oddzielonej przecinkami liście Id, jak to:

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

I możesz użyć tej funkcji wewnątrz klauzuli WHERE, jak to:

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

To był jedyny sposób na przekazanie parametru" array " do procedury.

 3
Author: chuckedw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-20 14:36:49
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
Author: Sagar Gangwal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-03 11:55:27

Obecnie użycie tablicy JSON byłoby oczywistą odpowiedzią.

Ponieważ jest to stare, ale wciąż aktualne pytanie, przedstawiłem krótki przykład. Funkcje JSON są dostępne od mySQL 5.7.x / MariaDB 10.2.3

Wolę to rozwiązanie niż ELT (), ponieważ jest to bardziej tablica i ta 'tablica' może być ponownie użyta w kodzie.

ale uważaj: to (JSON) jest z pewnością znacznie wolniejsze niż używanie tabeli tymczasowej. Jest po prostu bardziej poręczny. imo.

Tutaj jest jak używać tablicy 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

A tutaj mały przykład, aby pokazać, jak to działa w funkcji / procedurze:

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();
 3
Author: SeparateReality,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-15 08:34:52

To działa dobrze dla listy wartości:

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
Author: user2664904,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-21 15:44:45

Obie wersje używające zestawów nie działały dla mnie (testowane z MySQL 5.5). Funkcja ELT () zwraca cały zestaw. Biorąc pod uwagę, że instrukcja WHILE jest dostępna tylko w kontekście procedury, dodałem ją do mojego rozwiązania:

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__;
Szczerze mówiąc, nie sądzę, żeby to była dobra praktyka. Nawet jeśli jest to naprawdę konieczne, jest to ledwo czytelne i dość powolne.
 1
Author: jmmeier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-30 10:04:23

Czy sens tablic nie jest skuteczny? Jeśli tylko iterujesz wartości, myślę, że kursor na tymczasowej (lub stałej) tabeli ma więcej sensu niż szukanie przecinków, nie? Również czystsze. Lookup "mysql DECLARE CURSOR".

Dla dostępu losowego tymczasowa tabela z numerycznie indeksowanym kluczem głównym. Niestety najszybszy dostęp otrzymasz jest tabela hash, a nie prawdziwy dostęp losowy.

 1
Author: Amaigus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-26 21:18:45

W MYSQL w wersji po 5.7.x, możesz użyć JSON type do przechowywania tablicy. Wartość tablicy można uzyskać za pomocą klucza poprzez MYSQL.

 0
Author: nick darn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-29 00:48:34

Zainspirowany funkcją ELT (Numer indeksu, string1, string2, string3,...), myślę, że poniższy przykład działa jako przykład tablicy:

set @i := 1;
while @i <= 3
do
  insert into table(val) values (ELT(@i ,'val1','val2','val3'...));
set @i = @i + 1;
end while;
Mam nadzieję, że to pomoże.
 0
Author: edward,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-02 08:36:47

Wiem, że jest to trochę spóźniona odpowiedź, ale ostatnio musiałem rozwiązać podobny problem i pomyślałem, że może to być przydatne dla innych.

Tło

Rozważmy poniższą tabelę nazwaną "mytable":

Tabela początkowa

Problem polegał na przechowywaniu tylko ostatnich 3 rekordów i usuwaniu starszych rekordów, których systemid=1 (w tabeli może być wiele innych rekordów z innymi wartościami systemid)

Byłoby dobrze, gdybyś mógł to zrobić po prostu korzystanie z instrukcji

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

Jednak nie jest to jeszcze obsługiwane w MySQL i jeśli spróbujesz tego, otrzymasz błąd jak

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

Dlatego potrzebne jest obejście, w którym tablica wartości jest przekazywana do selektora IN przy użyciu zmiennej. Jednak, ponieważ zmienne muszą być pojedynczymi wartościami, musiałbym symulować tablicę. sztuką jest utworzenie tablicy jako oddzielonej przecinkami listy wartości (string) i przypisanie jej do zmiennej w następujący sposób

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);

The wynik zapisany w @myvar to

5,6,7

Następnie selektor FIND_IN_SET służy do wyboru z symulowanej tablicy

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

Łączny wynik końcowy jest następujący:

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);
Zdaję sobie sprawę, że jest to bardzo szczególny przypadek. Można go jednak zmodyfikować tak, aby pasował do każdego innego przypadku, w którym zmienna musi przechowywać tablicę wartości. Mam nadzieję, że to pomoże.
 0
Author: Clinton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-18 09:50:42

Myślę, że mogę poprawić tę odpowiedź. Spróbuj tego:

Parametr "Pranks" jest plikiem CSV. ie. '1,2,3,4.....etc "

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;

Ta metoda sprawia, że wyszukiwany ciąg wartości CSV jest coraz krótszy z każdą iteracją pętli, co moim zdaniem byłoby lepsze dla optymalizacji.

 -1
Author: user2288580,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-26 11:29:26

Próbowałeś użyć funkcji serialize () PHP? To pozwala na przechowywanie zawartości tablicy zmiennej w łańcuchu znaków PHP rozumie i jest bezpieczne dla bazy danych(zakładając, że wcześniej ją uniknąłeś).

$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());

Możesz również zrobić dokładnie to samo bez tablicy numerowanej

$array2 = array(
    'something' => 'something else'
);

Lub

$array3 = array(
    'somethingNew'
);
 -4
Author: Magictallguy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-23 13:45:35