ฉันจะจำลองตัวแปรอาร์เรย์ใน MySQL ได้อย่างไร


93

มันปรากฏว่า MySQL ไม่ได้มีตัวแปรอาร์เรย์ ฉันควรใช้อะไรแทน?


ดูเหมือนจะมีสองทางเลือกแนะนำ: เป็นชุดชนิดสเกลาร์และตารางชั่วคราว คำถามที่ฉันเชื่อมโยงเพื่อแนะนำอดีต แต่การใช้ตัวแปรเหล่านี้แทนตัวแปรอาร์เรย์เป็นวิธีปฏิบัติที่ดีหรือไม่? หรืออีกวิธีหนึ่งคือถ้าฉันไปกับชุดคำศัพท์ set-based idiom จะเทียบเท่ากับforeachอะไร

คำตอบ:


78

ฉันใช้ตารางชั่วคราวแทนตัวแปรอาร์เรย์ ไม่ใช่วิธีแก้ปัญหาที่ดีที่สุด แต่ได้ผล

โปรดทราบว่าคุณไม่จำเป็นต้องกำหนดฟิลด์อย่างเป็นทางการเพียงแค่สร้างโดยใช้ SELECT:

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

(ดูการสร้างตารางชั่วคราวจากคำสั่งที่เลือกโดยไม่ต้องใช้สร้างตาราง )


1
โอ้ฉันไม่รู้ว่า SQL มีสิ่งนี้ !! ตารางจะมีชีวิตอยู่สำหรับขอบเขตของการสืบค้นอัลที่รันเท่านั้น เรียบร้อย!
iGbanam

2
@ Yasky นั่นคือหากคุณไม่ใช้การเชื่อมต่อซ้ำ เพราะมันจะคงอยู่ตลอดทั้งเซสชั่น
Pacerier

1
@einpoklum - dev.mysql.com/doc/refman/5.0/en/tem Contemporary-table- pro issues.html
John

4
@ จอห์น: ใช่คุณสามารถใช้ซ้ำได้ แต่ไม่ใช่ในแบบสอบถามเดียวกัน
einpoklum

1
หากคุณกำลังใช้การเชื่อมต่อซ้ำโดยทั่วไปคุณควรวางตารางชั่วคราวหากมี my_temp_table; ก่อนสร้าง
ออเรสต์

46

คุณสามารถบรรลุสิ่งนี้ได้ใน MySQL โดยใช้WHILEloop:

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

4
ไม่สามารถวนซ้ำได้เฉพาะในกระบวนงานที่จัดเก็บไว้ใช่หรือไม่?
einpoklum

2
ใช่ถูกต้องเป็นไปได้ภายในกระบวนงานฟังก์ชันและทริกเกอร์ที่จัดเก็บไว้
Omesh

1
ดังนั้นฉันจึงไม่สามารถใช้รหัสที่คุณให้มาได้ ... ฉันต้องการสิ่งที่ใช้ได้โดยทั่วไปมากกว่า
einpoklum

คุณสามารถเขียนขั้นตอนการจัดเก็บตัวอย่างและCALLมัน
Omesh

1
ฉันไม่คิดว่าจำเป็นต้องมีอาร์เรย์ คุณสามารถทำได้อย่างง่ายดายโดยใช้ตารางชั่วคราวหรือUNION ALLไม่ใช้ขั้นตอน
Omesh

28

ลองใช้ฟังก์ชัน FIND_IN_SET () ของ MySql เช่น

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

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

หมายเหตุ: คุณไม่จำเป็นต้องตั้งค่าตัวแปรใน StoredProcedure หากคุณกำลังส่งผ่านพารามิเตอร์ด้วยค่า CSV


ระวังขีดจำกัดความยาวซึ่งอาจค่อนข้างต่ำ: stackoverflow.com/q/2567000/1333493
Nemo

19

ปัจจุบันการใช้อาร์เรย์ JSONเป็นคำตอบที่ชัดเจน

เนื่องจากนี่เป็นคำถามเก่า แต่ยังคงมีความเกี่ยวข้องฉันจึงสร้างตัวอย่างสั้น ๆ ฟังก์ชัน JSON พร้อมใช้งานตั้งแต่ mySQL 5.7.x / MariaDB 10.2.3

ฉันชอบโซลูชันนี้มากกว่า 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();

คุณกำลังบอกว่า ELT ช้ากว่าหรือ JSON ช้ากว่า?
Kanagavelu Sugumar

2
@Kanagavelu Sugumar: เวลาเขียน JSON ช้ากว่าแน่นอน ฉันแก้ไขคำตอบเพื่อให้ชัดเจนขึ้น
SeparateReality

16

ไม่รู้เกี่ยวกับอาร์เรย์ แต่มีวิธีการจัดเก็บรายการที่คั่นด้วยจุลภาคในคอลัมน์ VARCHAR ปกติ

และเมื่อคุณต้องการที่จะหาบางสิ่งบางอย่างในรายการที่คุณสามารถใช้FIND_IN_SET ()ฟังก์ชั่น


ถ้าฉันต้องการค้นหาเซตย่อยในเซตมีวิธีใดบ้าง?
Akshay Vishnoi

ขออภัย! ฉันไม่แน่ใจว่ามันเป็นไปได้
wormhit

คุณมีทางออกที่ดีที่สุด
Calvin

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

2
โปรดอธิบายว่าจะใช้รหัสนี้อย่างไรและตอบคำถามอย่างไร
einpoklum

ในตอนนี้คุณทำโพรซีเดอร์ง่ายๆซึ่งให้ทีละองค์ประกอบของสตริงนั้นซึ่งทำงานเป็นอาร์เรย์ใน oracle
Sagar Gangwal

ออราเคิล? คำถามนี้ไม่เกี่ยวกับ Oracle นอกจากนี้ดูเหมือนว่าคุณกำลังกำหนดอาร์เรย์ภายในขั้นตอน
einpoklum

โปรดตรวจสอบ Syntex สำหรับ mysql เท่านั้น
Sagar Gangwal

อย่าพลาดจุลภาคสุดท้าย!
Eagle_Eye

4

ฉันรู้ว่านี่เป็นการตอบกลับที่ล่าช้าเล็กน้อย แต่เมื่อเร็ว ๆ นี้ฉันต้องแก้ปัญหาที่คล้ายกันและคิดว่านี่อาจเป็นประโยชน์กับคนอื่น ๆ

พื้นหลัง

พิจารณาตารางด้านล่างที่เรียกว่า '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);

ฉันทราบดีว่านี่เป็นกรณีที่เฉพาะเจาะจงมาก อย่างไรก็ตามสามารถแก้ไขให้เหมาะกับกรณีอื่น ๆ ที่ตัวแปรต้องการเก็บอาร์เรย์ของค่า

ฉันหวังว่าสิ่งนี้จะช่วยได้


3

อาจสร้างตารางหน่วยความจำชั่วคราวที่มีคอลัมน์ (คีย์ค่า) หากคุณต้องการอาร์เรย์ที่เชื่อมโยงกัน การมีตารางหน่วยความจำเป็นสิ่งที่ใกล้เคียงที่สุดกับการมีอาร์เรย์ใน mysql


อืมฉันไม่ต้องการอาร์เรย์ที่เชื่อมโยงกันเพียงแค่อาร์เรย์
einpoklum

คุณสามารถใช้ตารางหน่วยความจำชั่วคราวที่มีเพียงคอลัมน์เดียวแล้ววนซ้ำค่าโดยใช้เคอร์เซอร์นั่นคือสิ่งที่ใกล้เคียงที่สุดในการใช้อาร์เรย์และสำหรับ / ในขณะที่ลูปในภาษาโปรแกรมที่ไม่ประกาศ
Pavle Lekic

ภาษามีคุณสมบัตินี้จริง ๆ กล่าวคือไม่มีเหตุผลทางวากยสัมพันธ์ที่คุณไม่ควรเลือกเวกเตอร์เป็นตัวแปรเหมือนกับที่คุณเลือกสเกลาร์ลงไป
einpoklum

3

นี่คือวิธีที่ฉันทำ

ขั้นแรกฉันสร้างฟังก์ชันที่ตรวจสอบว่า 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 clause ได้ดังนี้:

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

นี่เป็นวิธีเดียวที่ฉันพบในการส่งผ่านพารามิเตอร์ "อาร์เรย์" ไปยังกระบวนการ


2

จุดของอาร์เรย์จะมีประสิทธิภาพไม่ใช่หรือ หากคุณแค่ทำซ้ำตามค่าฉันคิดว่าเคอร์เซอร์บนตารางชั่วคราว (หรือถาวร) มีความหมายมากกว่าการค้นหาลูกน้ำใช่ไหม ยังสะอาดกว่า ค้นหา "mysql DECLARE CURSOR"

สำหรับการเข้าถึงแบบสุ่มตารางชั่วคราวที่มีคีย์หลักที่จัดทำดัชนีเป็นตัวเลข น่าเสียดายที่การเข้าถึงที่เร็วที่สุดที่คุณจะได้รับคือตารางแฮชไม่ใช่การเข้าถึงแบบสุ่มที่แท้จริง


นี่คือความคิดเห็นไม่ใช่คำตอบ ฉันไม่ได้ระบุว่านั่นคือสิ่งที่ฉันต้องการทำกับอาร์เรย์
einpoklum

2

ฉันแปลกใจที่ไม่มีคำตอบใดที่พูดถึง 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)

1

ใช้งานได้ดีสำหรับรายการค่า:

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

ทั้งสองเวอร์ชันที่ใช้ชุดไม่ได้ผลสำหรับฉัน (ทดสอบด้วย MySQL 5.5) ฟังก์ชัน ELT () ส่งคืนทั้งชุด การพิจารณาคำสั่งในขณะที่สามารถใช้ได้ในบริบทกระบวนการเท่านั้นฉันเพิ่มมันลงในโซลูชันของฉัน:

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

อีกวิธีหนึ่งในการดูปัญหาเดียวกัน หวังว่าจะเป็นประโยชน์

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

อันที่จริงมันไม่ชัดเจนว่าคุณพูดอะไรที่นี่ คำถามนี้เมื่อ 7 ปีที่แล้วมีคำตอบที่เกี่ยวข้องหลายประการ ลองลบคำตอบของคุณออกหรืออธิบายสิ่งที่คุณพยายามจะบอกเราโดยตรง / โดยทั่วไปมากกว่าผ่านทางตัวอย่าง
einpoklum

0

ใน MYSQL เวอร์ชันหลัง 5.7.x คุณสามารถใช้ประเภท JSON เพื่อจัดเก็บอาร์เรย์ได้ คุณสามารถรับค่าของอาร์เรย์ด้วยคีย์ผ่าน MYSQL


8
คุณช่วยยกตัวอย่างได้ไหมว่าฉันจะทำสิ่งนี้
einpoklum

0

แรงบันดาลใจจากฟังก์ชัน ELT (หมายเลขดัชนี, 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;

หวังว่ามันจะช่วยได้


0

นี่คือตัวอย่างสำหรับ 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


มีการแนะนำสตริงที่คั่นด้วยจุลภาคเมื่อหลายปีก่อน
einpoklum

@einpoklum ใช่ - และนี่เป็นอีกวิธีหนึ่งในการโต้ตอบกับพวกเขา
Ulad Kasach

0

ตัวแปรอาร์เรย์จำเป็นจริงหรือ?

ฉันถามเพราะเดิมทีฉันมาที่นี่เพื่อเพิ่มอาร์เรย์เป็นตัวแปรตาราง 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 เป็นคีย์ต่างประเทศ กล่าวอีกนัยหนึ่งมันเป็นคีย์หลักในตารางอื่นโดยเฉพาะฟิลด์ 'ชื่อ' ในตารางบุคคลซึ่งเป็นสิ่งที่ REFERENCES หมายถึง

นอกจากนี้เรายังกำหนดให้การรวมกันของบุคคลและรายการเป็นคีย์หลัก แต่ในทางเทคนิคแล้วไม่จำเป็น

สุดท้ายหากคุณต้องการรับรายการทั้งหมดในรายการของบุคคลคุณสามารถเรียกใช้แบบสอบถามนี้:

SELECT item FROM BuyList WHERE person='John';

สิ่งนี้ช่วยให้คุณมีรายการทั้งหมดในรายการของ John ไม่จำเป็นต้องมีอาร์เรย์!


วิธีแก้ปัญหาที่ฉันยอมรับคือการใช้ตารางชั่วคราว
einpoklum

แน่นอน. จากนั้นฉันก็รวมคำตอบนี้สำหรับทุกคนเช่นฉันที่มาที่หน้านี้เพื่อหาวิธีสร้างประเภทอาร์เรย์ - ซึ่งในตอนแรกไม่เข้าใจว่าทำไมอาร์เรย์จึงไม่ใช่ประเภทใน MySQL ดูเหมือนว่าโดยการออกแบบ กรณีทั่วไปไม่ได้แสดงไว้ที่นี่ดังนั้นฉันจึงรวมสิ่งที่เรียนรู้เพื่อให้คนอื่นเข้าใจว่าโดยทั่วไปไม่จำเป็นต้องใช้อาร์เรย์ ฉันไม่คาดหวังว่าคุณจะเลือกคำตอบของฉัน ขึ้นอยู่กับกรณีการใช้งาน คุณมีคำตอบที่ยอมรับสำหรับกรณีการใช้งานที่เฉพาะเจาะจงและฉันกำลังให้คำตอบนี้สำหรับกรณีการใช้งานทั่วไป
kraftydevil

0

ถ้าเรามีโต๊ะเดียวแบบนั้น

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         |
+------------+-------+--------+------------+-------------+

ฉันคิดว่าคำตอบของคลินตันซ้ำกัน
einpoklum

-2

ฉันคิดว่าฉันสามารถปรับปรุงคำตอบนี้ได้ ลองสิ่งนี้:

พารามิเตอร์ "Pranks" คือ 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 สั้นลงเรื่อย ๆ เมื่อมีการวนซ้ำแต่ละครั้งซึ่งฉันเชื่อว่าจะดีกว่าสำหรับการเพิ่มประสิทธิภาพ


'คำตอบนี้' ที่คุณหมายถึงคืออะไร? นอกจากนี้คุณไม่จำเป็นต้องมีไฟล์ CSV
einpoklum

ฉันไม่ได้อ้างถึงไฟล์ CSV ฉันอ้างถึงค่า CSV เช่น '1,2,3,4 ... etc'
user2288580

-2

ฉันจะลองสิ่งนี้สำหรับหลาย ๆ คอลเลกชัน ฉันเป็นผู้เริ่มต้น 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');

ฉันคิดว่าฉันเข้าใจสิ่งที่คุณแนะนำ แต่มันค่อนข้างเทอะทะและอาจช้าอย่างไม่น่าเชื่อด้วย ...
einpoklum

ฉันไม่สามารถรับรองหรือยกเลิกได้โดยไม่ต้องทดสอบความเครียด โดยพื้นฐานแล้วเป็นการค้นหาคีย์หลักและการแทรกบนตารางชั่วคราว (หรือตารางปกติ) ฉันจะใช้มันจนกว่าฉันจะพบปัญหาหรือพบวิธีที่ดีกว่า แต่ฉันทำสิ่งแปลก ๆ เช่นเขียนคอมไพเลอร์และเกมอย่างสมบูรณ์ใน Oracle PL / SQL
Dave

-2

แทนที่จะบันทึกข้อมูลเป็นอาร์เรย์หรือในแถวเดียวคุณควรสร้างแถวที่แตกต่างกันสำหรับทุกค่าที่ได้รับ วิธีนี้จะทำให้เข้าใจง่ายขึ้นมากแทนที่จะรวมทั้งหมดเข้าด้วยกัน


โปรดแบ่งปันวิธีการทำ
Nico Haase

-5

คุณได้ลองใช้ Serialize () ของ PHP แล้วหรือยัง? ซึ่งช่วยให้คุณสามารถจัดเก็บเนื้อหาของอาร์เรย์ของตัวแปรในสตริงที่ 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'
);

8
ฉันไม่ได้ทำงานกับ PHP ดังนั้นจึงไม่เกี่ยวข้องกับฉันจริงๆ
einpoklum

2
ใช้ JSON แทนการทำให้เป็นอนุกรม เป็นภาษาทั่วไปและไม่ขึ้นกับภาษา
Rick James
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.