Il semble que MySQL n'a pas de variables de tableau. Que dois-je utiliser à la place?
Il semble y avoir deux alternatives suggérées: un scalaire de type ensemble et des tables temporaires . La question à laquelle j'ai lié suggère la première. Mais est-ce une bonne pratique de les utiliser au lieu de variables de tableau? Sinon, si j'utilise des ensembles, quel serait l'équivalent de l'idiome basé sur les ensembles foreach
?
ELT
.Réponses:
Eh bien, j'ai utilisé des tables temporaires au lieu de variables de tableau. Ce n'est pas la meilleure solution, mais cela fonctionne.
Notez que vous n'avez pas besoin de définir formellement leurs champs, créez-les simplement à l'aide d'un SELECT:
CREATE TEMPORARY TABLE IF NOT EXISTS my_temp_table SELECT first_name FROM people WHERE last_name = 'Smith';
(Voir aussi Créer une table temporaire à partir de l'instruction select sans utiliser Create Table .)
la source
Vous pouvez y parvenir dans MySQL en utilisant la
WHILE
boucle: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;
EDIT: Vous pouvez également le faire en utilisant
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 ... );
la source
CALL
cela.UNION ALL
sans utiliser de procédure.Essayez d'utiliser la fonction FIND_IN_SET () de MySql par exemple
SET @c = 'xxx,yyy,zzz'; SELECT * from countries WHERE FIND_IN_SET(countryname,@c);
Remarque: vous n'avez pas à définir la variable dans StoredProcedure si vous transmettez des paramètres avec des valeurs CSV.
la source
De nos jours, l'utilisation d'un tableau JSON serait une réponse évidente.
Comme il s'agit d'une question ancienne mais toujours pertinente, j'ai produit un court exemple. Les fonctions JSON sont disponibles depuis mySQL 5.7.x / MariaDB 10.2.3
Je préfère cette solution à ELT () car c'est vraiment plus comme un tableau et ce «tableau» peut être réutilisé dans le code.
Mais attention: il (JSON) est certainement beaucoup plus lent que l'utilisation d'une table temporaire. C'est juste plus pratique. imo.
Voici comment utiliser un tableau 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
Et voici un petit exemple pour montrer comment cela fonctionne dans une fonction / procédure:
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();
la source
Je ne connais pas les tableaux, mais il existe un moyen de stocker des listes séparées par des virgules dans la colonne VARCHAR normale.
Et lorsque vous avez besoin de trouver quelque chose dans cette liste, vous pouvez utiliser la fonction FIND_IN_SET () .
la source
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 ;
la source
Je sais que c'est un peu une réponse tardive, mais j'ai récemment dû résoudre un problème similaire et j'ai pensé que cela pourrait être utile à d'autres.
Contexte
Considérez le tableau ci-dessous appelé «mytable»:
Le problème était de ne conserver que les 3 derniers enregistrements et de supprimer tous les enregistrements plus anciens dont l'ID système = 1 (il pourrait y avoir de nombreux autres enregistrements dans la table avec d'autres valeurs ID système)
Ce serait bien que vous puissiez le faire simplement en utilisant la déclaration
DELETE FROM mytable WHERE id IN (SELECT id FROM `mytable` WHERE systemid=1 ORDER BY id DESC LIMIT 3)
Cependant, cela n'est pas encore pris en charge dans MySQL et si vous essayez ceci, vous obtiendrez une erreur comme
Une solution de contournement est donc nécessaire par laquelle un tableau de valeurs est passé au sélecteur IN à l'aide d'une variable. Cependant, comme les variables doivent être des valeurs uniques, je devrais simuler un tableau . L'astuce consiste à créer le tableau sous forme de liste de valeurs séparées par des virgules (chaîne) et à l'affecter à la variable comme suit
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);
Le résultat stocké dans @myvar est
Ensuite, le sélecteur FIND_IN_SET est utilisé pour sélectionner dans le tableau simulé
SELECT * FROM mytable WHERE FIND_IN_SET(id,@myvar);
Le résultat final combiné est le suivant:
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);
Je suis conscient qu'il s'agit d'un cas très particulier. Cependant, il peut être modifié pour convenir à n'importe quel autre cas où une variable doit stocker un tableau de valeurs.
J'espère que ca aide.
la source
Peut-être créer une table mémoire temporaire avec des colonnes (clé, valeur) si vous voulez des tableaux associatifs. Avoir une table mémoire est la chose la plus proche d'avoir des tableaux dans mysql
la source
Voici comment je l'ai fait.
Tout d'abord, j'ai créé une fonction qui vérifie si une valeur Long / Integer / quelle que soit la valeur se trouve dans une liste de valeurs séparées par des virgules:
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;
Vous pouvez maintenant rechercher un identifiant dans une liste d'identifiants séparés par des virgules, comme ceci:
select `is_id_in_ids`('1001,1002,1003',1002);
Et vous pouvez utiliser cette fonction dans une clause WHERE, comme ceci:
SELECT * FROM table1 WHERE `is_id_in_ids`('1001,1002,1003',table1_id);
C'était le seul moyen que j'ai trouvé pour passer un paramètre "array" à une PROCEDURE.
la source
Le but des tableaux n'est-il pas d'être efficace? Si vous ne faites que parcourir des valeurs, je pense qu'un curseur sur une table temporaire (ou permanente) a plus de sens que de chercher des virgules, non? Aussi plus propre. Recherchez "mysql DECLARE CURSOR".
Pour un accès aléatoire, une table temporaire avec une clé primaire indexée numériquement. Malheureusement, l'accès le plus rapide que vous obtiendrez est une table de hachage, pas un véritable accès aléatoire.
la source
Je suis surpris qu'aucune des réponses ne mentionne ELT / FIELD.
ELT / FIELD fonctionne très similaire à un tableau, surtout si vous avez des données statiques.
FIND_IN_SET fonctionne également de manière similaire mais n'a pas de fonction complémentaire intégrée mais il est assez facile d'en écrire une.
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)
la source
Cela fonctionne bien pour la liste de valeurs:
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;
la source
Les deux versions utilisant des ensembles ne fonctionnaient pas pour moi (testé avec MySQL 5.5). La fonction ELT () retourne l'ensemble complet. Étant donné que l'instruction WHILE n'est disponible que dans le contexte PROCEDURE, je l'ai ajoutée à ma solution:
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__;
Pour être honnête, je ne pense pas que ce soit une bonne pratique. Même si c'est vraiment nécessaire, c'est à peine lisible et assez lent.
la source
Une autre façon de voir le même problème. J'espère utile
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,');
la source
Dans la version MYSQL après 5.7.x, vous pouvez utiliser le type JSON pour stocker un tableau. Vous pouvez obtenir la valeur d'un tableau par une clé via MYSQL.
la source
Inspiré de la fonction ELT (numéro d'index, string1, string2, string3,…), je pense que l'exemple suivant fonctionne comme un exemple de tableau:
set @i := 1; while @i <= 3 do insert into table(val) values (ELT(@i ,'val1','val2','val3'...)); set @i = @i + 1; end while;
J'espère que cela aidera.
la source
Voici un exemple pour MySQL pour boucler une chaîne délimitée par des virgules.
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;
cela utilise la
get_from_delimiter_split_string
fonction définie ici: https://stackoverflow.com/a/59666211/3068233la source
Une variable de tableau est-elle vraiment nécessaire?
Je demande parce que j'ai atterri ici à l'origine voulant ajouter un tableau en tant que variable de table MySQL. J'étais relativement nouveau dans la conception de bases de données et j'essayais de penser à la façon dont je le ferais dans un langage de programmation typique.
Mais les bases de données sont différentes. J'ai pensé je voulais un tableau comme variable, mais il s'avère que ce n'est tout simplement pas une pratique courante de base de données MySQL.
Pratique standard
La solution alternative aux tableaux consiste à ajouter une table supplémentaire, puis à référencer votre table d'origine avec une clé étrangère.
À titre d'exemple, imaginons une application qui garde la trace de tous les articles que chaque personne d'un ménage souhaite acheter au magasin.
Les commandes pour créer la table que j'avais initialement envisagées auraient ressemblé à ceci:
#doesn't work CREATE TABLE Person( name VARCHAR(50) PRIMARY KEY buy_list ARRAY );
Je pense que j'ai envisagé buy_list comme une chaîne d'éléments séparés par des virgules ou quelque chose comme ça.
Mais MySQL n'a pas de champ de type tableau, donc j'avais vraiment besoin de quelque chose comme ceci:
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) );
Ici, nous définissons une contrainte nommée fk_person. Il indique que le champ «personne» dans BuyList est une clé étrangère. En d'autres termes, il s'agit d'une clé primaire dans une autre table, en particulier le champ «nom» de la table Personne, ce que REFERENCES indique.
Nous avons également défini la combinaison de la personne et de l'élément comme étant la clé primaire, mais techniquement ce n'est pas nécessaire.
Enfin, si vous souhaitez obtenir tous les éléments de la liste d'une personne, vous pouvez exécuter cette requête:
SELECT item FROM BuyList WHERE person='John';
Cela vous donne tous les éléments de la liste de John. Aucun tableau nécessaire!
la source
Si nous avons une table comme celle-là
mysql> select * from user_mail; +------------+-------+ | email | user | +------------+-------+- | email1@gmail | 1 | | email2@gmail | 2 | +------------+-------+--------+------------+
et la table de tableau:
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 |
Nous pouvons sélectionner les lignes de la deuxième table comme un tableau avec la fonction 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 | +------------+-------+--------+------------+-------------+
la source
Je pense que je peux améliorer cette réponse. Essaye ça:
Le paramètre «Pranks» est un CSV. c'est à dire. «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;
Cette méthode raccourcit progressivement la chaîne de valeurs CSV recherchée à chaque itération de la boucle, ce qui, je pense, serait préférable pour l'optimisation.
la source
J'essaierais quelque chose comme ça pour plusieurs collections. Je suis un débutant MySQL. Désolé pour les noms de fonction, je n'ai pas pu décider quels noms seraient les meilleurs.
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');
la source
Plutôt que d'enregistrer les données sous forme de tableau ou sur une seule ligne, vous devez créer des lignes différentes pour chaque valeur reçue. Cela rendra les choses beaucoup plus simples à comprendre plutôt que de tout rassembler.
la source
Avez-vous essayé d'utiliser serialize () de PHP? Cela vous permet de stocker le contenu du tableau d'une variable dans une chaîne que PHP comprend et est sans danger pour la base de données (en supposant que vous l'ayez d'abord échappé).
$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());
Vous pouvez également faire exactement la même chose sans tableau numéroté
ou
la source