รวมส่วนหัวเมื่อใช้ SELECT INTO OUTFILE?


คำตอบ:


166

คุณต้องฮาร์ดโค้ดส่วนหัวเหล่านั้นด้วยตัวเอง สิ่งที่ต้องการ:

SELECT 'ColName1', 'ColName2', 'ColName3'
UNION ALL
SELECT ColName1, ColName2, ColName3
    FROM YourTable
    INTO OUTFILE '/path/outfile'

14
แต่จะใช้ไม่ได้ถ้ามีORDER BYในSELECTclause. บรรทัดส่วนหัวสามารถอยู่ที่ใดก็ได้ในไฟล์ที่สร้างขึ้นโดยขึ้นอยู่กับลำดับ
COil

ดูคำตอบด้านล่างสำหรับแนวคิดในการใช้ ORDER BY และคำตอบของ Matt สำหรับวิธีรับ ColName1, ColName2 และอื่น ๆ ทั้งหมดอย่างรวดเร็วส่วนเสริมที่มีประโยชน์มากสำหรับคำตอบนี้
Andrew T

1
คำตอบนี้ดูเหมือนจะถูกต้องฉันยังใช้มันเกือบจะสุ่มสี่สุ่มห้าบนเซิร์ฟเวอร์ dev ของฉัน ... หากไม่มีส่วนหัวคอลัมน์จะใช้เวลาประมาณ 50 วินาทีในการถ่ายโอนข้อมูล 240 ล้านบรรทัด ด้วย UNION ALL นี้เซิร์ฟเวอร์กำลังประสบปัญหาใหญ่ในการพยายามทำตารางชั่วคราวก่อนที่จะทิ้งทุกอย่างตอนนี้ใช้เวลาเกิน 10 นาทีแล้วและยังคงรอให้ตารางชั่วคราวเขียนบนดิสก์! ระวัง! คุณต้องการเพิ่มชื่อคอลัมน์ด้วยวิธีอื่นแม้ว่าจะหมายถึงการเปิดไฟล์หลังจากนั้นด้วยภาษาโปรแกรมอื่นก็ตาม
Salketer

สิ่งนี้จะใช้งานได้ก็ต่อเมื่อคอลัมน์ทั้งหมดของ YourTable เป็นประเภทข้อมูลอักขระซึ่งเหมาะสม มิฉะนั้นคุณจะได้รับข้อผิดพลาดที่ไม่เป็นประโยชน์: "คำสั่ง SELECT ที่ใช้มีจำนวนคอลัมน์ต่างกัน"
TheBamf

1
นี่เป็นสาเหตุหนึ่งที่ฉันกำลังพิจารณาเปลี่ยนไปใช้ DBMS อื่น
e18r

85

วิธีแก้ปัญหาโดย Joe Steanelli ใช้งานได้ แต่การสร้างรายการคอลัมน์นั้นไม่สะดวกเมื่อเกี่ยวข้องกับคอลัมน์หลายสิบหรือหลายร้อยคอลัมน์ นี่คือวิธีการที่จะได้รับรายชื่อคอลัมน์ของตารางmy_tableในmy_schema

-- override GROUP_CONCAT limit of 1024 characters to avoid a truncated result
set session group_concat_max_len = 1000000;

select GROUP_CONCAT(CONCAT("'",COLUMN_NAME,"'"))
from INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'my_table'
AND TABLE_SCHEMA = 'my_schema'
order BY ORDINAL_POSITION

ตอนนี้คุณสามารถคัดลอกและวางแถวผลลัพธ์เป็นคำสั่งแรกในวิธีการของ Joe


2
สิ่งนี้ส่งคืนคอลัมน์ทั้งหมดที่เชื่อมต่อกันในฟิลด์เดียว ฉันไม่สามารถเชื่อมโยงกับคำสั่ง select อื่นที่ส่งคืนหลายฟิลด์ สิ่งนี้มีประโยชน์มากสำหรับการรับบรรทัดที่ฉันสามารถคัดลอกและวางเป็นส่วนหัวของไฟล์ outfile ของฉันได้
tmoore82

1
แนวคิดคือการคัดลอกช่องเดียวที่เป็นผลลัพธ์และวางลงในคำสั่ง UNION ของคุณแทนที่จะพิมพ์ด้วยตนเองใน column1, column2 และอื่น ๆ เมื่อใช้วิธีของ Joe (คำตอบที่ยอมรับ) เพื่อให้คุณสามารถรับรายการคอลัมน์ได้เร็วขึ้น!
Andrew T

ฉันต้องการส่งออกสคีมาตาราง / ฟิลด์ทั้งหมดของฉัน คำตอบนี้รวมกับคำตอบที่ได้รับการยอมรับทำให้เคล็ดลับ!
Rémi Breton

@Chris ORDER BY ORDINAL_POSITIONจัดการเรื่องนั้น
แมตต์

1
ORDER BY ORDINAL_POSITION ควรเป็นส่วนหนึ่งของการเรียก GROUP_CONCAT () เช่นเดียวกับในGROUP_CONCAT(CONCAT('"',COLUMN_NAME,'"') order BY ORDINAL_POSITION)
Apuleius

15

สำหรับการเลือกแบบซับซ้อนด้วย ORDER BY ฉันใช้สิ่งต่อไปนี้:

SELECT * FROM (
    SELECT 'Column name #1', 'Column name #2', 'Column name ##'
    UNION ALL
    (
        // complex SELECT statement with WHERE, ORDER BY, GROUP BY etc.
    )
) resulting_set
INTO OUTFILE '/path/to/file';

โซลูชันนี้ใช้งานได้ดีเมื่อสั่งเคียวรีที่สอง (ซับซ้อน) ถ้าคุณไม่ทำแบบนี้คุณจะต้องสั่งคอลัมน์แรกซึ่งไม่เป็นที่ต้องการ ข้อเสนอแนะที่ดี @evilguc!
Aaron

ไม่ได้ผลกับฉันหลังจากทำ UNION ALL ลำดับคอลัมน์ id ก็ยุ่งเหยิง
Mohanad Kaleia

6

คุณสามารถใช้คำสั่งที่เตรียมไว้กับคำตอบของ lucek และส่งออกตารางแบบไดนามิกพร้อมชื่อคอลัมน์ใน CSV:

--If your table has too many columns
SET GLOBAL group_concat_max_len = 100000000;
--Prepared statement
SET @SQL = ( select CONCAT('SELECT * INTO OUTFILE \'YOUR_PATH\' FIELDS TERMINATED BY \',\' OPTIONALLY ENCLOSED BY \'"\' ESCAPED BY \'\' LINES TERMINATED BY \'\\n\' FROM (SELECT ', GROUP_CONCAT(CONCAT("'",COLUMN_NAME,"'")),' UNION select * from YOUR_TABLE) as tmp') from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'YOUR_TABLE' AND TABLE_SCHEMA = 'YOUR_SCHEMA' order BY ORDINAL_POSITION );
--Execute it
PREPARE stmt FROM @SQL;
EXECUTE stmt;

ขอบคุณ lucek.


6

สิ่งนี้จะทำให้คุณได้สั่งซื้อคอลัมน์และ / หรือขีด จำกัด

SELECT 'ColName1', 'ColName2', 'ColName3'
UNION ALL
SELECT * from (SELECT ColName1, ColName2, ColName3
    FROM YourTable order by ColName1 limit 3) a
    INTO OUTFILE '/path/outfile';

1
โปรดทราบว่าเค้าโครงแบบสอบถามนี้ใช้ได้กับฉันใน MariaDB 10.1 เช่นกัน เค้าโครงอื่น ๆ ที่แนะนำในชุดข้อความนี้ไม่ได้
Programmer

แสดงส่วนหัวที่ด้านล่างด้วยเหตุผลบางประการ แต่ใช้งานได้ดีในการแสดงกลับด้านบนในแอปสเปรดชีตแปลก แต่น่าเชียร์
Dmitri DB

ฉันติดอยู่กับการใช้ mariaDB เช่นกัน ด้วยการแยกโอกาสในการขายเพียง 100 ครั้งซึ่งเร็วกว่าการเรียกใช้คำตอบที่ยอมรับเพียง 14 วินาที บัตรแรกที่ยอมรับ: Query OK, 100 rows affected (14.72 sec) บัตรที่สองสำหรับคุณQuery OK, 101 rows affected (0.00 sec)
Casper Wilkes

6

ฉันเพียงแค่สร้างแบบสอบถาม 2 รายการโดยอันดับแรกจะได้รับผลลัพธ์การสืบค้น (ขีด จำกัด 1) ด้วยชื่อคอลัมน์ (ไม่มีรหัสฮาร์ดไม่มีปัญหากับการเข้าร่วมลำดับโดยชื่อคอลัมน์ที่กำหนดเอง ฯลฯ ) และอย่างที่สองเพื่อสร้างแบบสอบถามเองและรวมไฟล์เป็น CSV เดียว ไฟล์:

CSVHEAD=`/usr/bin/mysql $CONNECTION_STRING -e "$QUERY limit 1;"|head -n1|xargs|sed -e "s/ /'\;'/g"`
echo "\'$CSVHEAD\'" > $TMP/head.txt
/usr/bin/mysql $CONNECTION_STRING -e "$QUERY into outfile '${TMP}/data.txt' fields terminated by ';' optionally enclosed by '\"' escaped by '' lines terminated by '\r\n';"
cat $TMP/head.txt $TMP/data.txt > $TMP/data.csv

5

ฉันประสบปัญหาที่คล้ายกันขณะเรียกใช้แบบสอบถาม mysql บนตารางขนาดใหญ่ใน NodeJS วิธีที่ฉันใช้เพื่อรวมส่วนหัวในไฟล์ CSV ของฉันมีดังนี้

  1. ใช้แบบสอบถาม OUTFILE เพื่อเตรียมไฟล์ที่ไม่มีส่วนหัว

        SELECT * INTO OUTFILE [FILE_NAME] FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED 
        BY '\"' LINES TERMINATED BY '\n' FROM [TABLE_NAME]
  2. ดึงข้อมูลส่วนหัวของคอลัมน์สำหรับตารางที่ใช้ในจุดที่ 1

        select GROUP_CONCAT(CONCAT(\"\",COLUMN_NAME,\"\")) as col_names from 
        INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = [TABLE_NAME] AND TABLE_SCHEMA 
        = [DATABASE_NAME] ORDER BY ORDINAL_POSITION
  3. ผนวกส่วนหัวของคอลัมน์เข้ากับไฟล์ที่สร้างในขั้นตอนที่ 1 โดยใช้แพ็กเกจ npm ที่นำหน้าไฟล์

การดำเนินการของแต่ละขั้นตอนถูกควบคุมโดยใช้สัญญาใน NodeJS


3

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

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

นี่คือวิธีที่ฉันใช้ R ต้องใช้ไลบรารี RMySQL:

db <- dbConnect(MySQL(), user='user', password='password', dbname='myschema', host='localhost')

query <- dbSendQuery(db, "select * from mytable")
dataset <- fetch(query, n=-1)

write.csv(dataset, 'mytable_backup.csv')

มันค่อนข้างโกง แต่ฉันพบว่านี่เป็นวิธีแก้ปัญหาอย่างรวดเร็วเมื่อจำนวนคอลัมน์ของฉันยาวเกินไปที่จะใช้วิธี concat ด้านบน หมายเหตุ: R จะเพิ่มคอลัมน์ 'row.names' ที่จุดเริ่มต้นของ CSV ดังนั้นคุณจะต้องวางคอลัมน์นั้นหากคุณจำเป็นต้องพึ่งพา CSV เพื่อสร้างตารางขึ้นใหม่


2

ดังนั้นหากคอลัมน์ทั้งหมดในmy_tableเป็นชนิดข้อมูลอักขระเราสามารถรวมคำตอบยอดนิยม (โดย Joe, matt และ evilguc) เข้าด้วยกันเพื่อให้ส่วนหัวถูกเพิ่มโดยอัตโนมัติในแบบสอบถาม SQL แบบ 'ง่าย' เช่น

select * from (
  (select column_name
    from information_schema.columns
    where table_name = 'my_table'
    and table_schema = 'my_schema'
    order by ordinal_position)
  union all
  (select *  // potentially complex SELECT statement with WHERE, ORDER BY, GROUP BY etc.
  from my_table)) as tbl
into outfile '/path/outfile'
fields terminated by ',' optionally enclosed by '"' escaped by '\\'
lines terminated by '\n';

โดยที่สองสามบรรทัดสุดท้ายสร้างเอาต์พุต csv

โปรดทราบว่าสิ่งนี้อาจช้าหากmy_tableมีขนาดใหญ่มาก


มีข้อผิดพลาด "คำสั่ง SELECT ที่ใช้มีจำนวนคอลัมน์ต่างกัน" Sir
Bowei Liu

1

ฉันคิดว่าถ้าคุณใช้ UNION มันจะได้ผล:

select 'header 1', 'header 2', ...
union
select col1, col2, ... from ...

ฉันไม่รู้วิธีระบุส่วนหัวด้วยไวยากรณ์ INTO OUTFILE โดยตรง


1
UNION ALLจะปลอดภัยและเร็วขึ้น
toxalot

1

จริงๆแล้วคุณสามารถทำให้มันใช้งานได้แม้จะมี ORDER BY ก็ตาม

เพียงแค่ต้องการกลอุบายบางอย่างในการเรียงลำดับตามคำสั่ง - เราใช้คำสั่ง case และแทนที่ค่าส่วนหัวด้วยค่าอื่น ๆ ที่รับประกันว่าจะเรียงลำดับก่อนในรายการ (เห็นได้ชัดว่าสิ่งนี้ขึ้นอยู่กับประเภทของฟิลด์และไม่ว่าคุณจะเรียง ASC หรือ DESC)

สมมติว่าคุณมีสามช่องชื่อ (varchar) is_active (bool) date_something_happens (วันที่) และคุณต้องการเรียงลำดับสองช่องที่สองจากมากไปหาน้อย:

select 
        'name'
      , 'is_active' as is_active
      , date_something_happens as 'date_something_happens'

 union all

 select name, is_active, date_something_happens

 from
    my_table

 order by
     (case is_active when 'is_active' then 0 else is_active end) desc
   , (case date when 'date' then '9999-12-30' else date end) desc

1

เนื่องจากฟังก์ชัน "include-headers" ดูเหมือนจะยังไม่เป็นแบบ build-in และ "โซลูชัน" ส่วนใหญ่ที่นี่จำเป็นต้องพิมพ์ชื่อคอลัมน์ด้วยตนเองและ / หรือไม่ได้คำนึงถึงการรวมเข้าด้วยกันฉันขอแนะนำให้ได้รับการแก้ไขปัญหา

  • ทางเลือกที่ดีที่สุดที่ฉันพบคือการใช้เครื่องมือที่เหมาะสม (ฉันใช้HeidiSQL )
    ใส่คำขอของคุณเลือกตารางเพียงคลิกขวาและส่งออกไปยังไฟล์ มีตัวเลือกที่จำเป็นทั้งหมดสำหรับการส่งออกที่สะอาด ans ควรจัดการกับความต้องการส่วนใหญ่

  • ในความคิดเดียวกันวิธี user3037511 ของการทำงานที่ดีและสามารถอัตโนมัติได้อย่างง่ายดาย
    เพียงแค่เปิดคำขอของคุณด้วยบรรทัดคำสั่งเพื่อรับส่วนหัวของคุณ คุณอาจได้รับข้อมูลด้วย SELECT INTO OUTFILE ... หรือเรียกใช้แบบสอบถามของคุณโดยไม่มีขีด จำกัด ให้คุณเลือก

    โปรดทราบว่าการเปลี่ยนเส้นทางเอาต์พุตไปยังไฟล์จะทำงานได้ดีทั้งบน Linux และ Windows


สิ่งนี้ทำให้ฉันต้องการเน้นว่า80% ของเวลาเมื่อฉันต้องการใช้ SELECT FROM INFILE หรือ SELECT INTO OUTFILE ฉันจะใช้อย่างอื่นเนื่องจากข้อ จำกัด บางประการ (ที่นี่ไม่มี 'ตัวเลือกส่วนหัว' ใน AWS-RDS สิทธิ์ที่ขาดหายไปและอื่น ๆ )

ดังนั้นฉันไม่ได้ตอบคำถามของ op อย่างตรงไปตรงมา... แต่ควรตอบสนองความต้องการของเขา:)
แก้ไข: และเพื่อตอบคำถามของเขาจริง ๆ : ไม่
ณ วันที่ 2017-09-07 คุณไม่สามารถรวมส่วนหัวได้หากคุณ ติดกับคำสั่ง SELECT INTO OUTFILE
: |


1

ตัวอย่างจากเซ็นเซอร์ชื่อตารางฐานข้อมูลของฉันพร้อมคอลัมน์ (id, เวลา, หน่วย)

select ('id') as id, ('time') as time, ('unit') as unit
UNION ALL
SELECT * INTO OUTFILE 'C:/Users/User/Downloads/data.csv'
  FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
  LINES TERMINATED BY '\n'
  FROM sensor

0

ฉันกำลังเขียนโค้ดของฉันใน PHP และฉันมีปัญหาเล็กน้อยในการใช้ฟังก์ชัน concat และ union และยังไม่ได้ใช้ตัวแปร SQL ด้วยวิธีใด ๆ ที่ฉันทำให้มันใช้งานได้นี่คือรหัสของฉัน:

//first I connected to the information_scheme DB

$headercon=mysqli_connect("localhost", "USERNAME", "PASSWORD", "information_schema");

//took the healders out in a string (I could not get the concat function to work, so I wrote a loop for it)

    $headers = '';
    $sql = "SELECT column_name AS columns FROM `COLUMNS` WHERE table_schema = 'YOUR_DB_NAME' AND table_name = 'YOUR_TABLE_NAME'";
    $result = $headercon->query($sql);
    while($row = $result->fetch_row())
    {
        $headers = $headers . "'" . $row[0] . "', ";
    }
$headers = substr("$headers", 0, -2);

// connect to the DB of interest

$con=mysqli_connect("localhost", "USERNAME", "PASSWORD", "YOUR_DB_NAME");

// export the results to csv
$sql4 = "SELECT $headers UNION SELECT * FROM YOUR_TABLE_NAME WHERE ... INTO OUTFILE '/output.csv' FIELDS TERMINATED BY ','";
$result4 = $con->query($sql4);

0

นี่คือวิธีรับชื่อส่วนหัวจากชื่อคอลัมน์แบบไดนามิก

/* Change table_name and database_name */
SET @table_name = 'table_name';
SET @table_schema = 'database_name';
SET @default_group_concat_max_len = (SELECT @@group_concat_max_len);

/* Sets Group Concat Max Limit larger for tables with a lot of columns */
SET SESSION group_concat_max_len = 1000000;

SET @col_names = (
  SELECT GROUP_CONCAT(QUOTE(`column_name`)) AS columns
  FROM information_schema.columns
  WHERE table_schema = @table_schema
  AND table_name = @table_name);

SET @cols = CONCAT('(SELECT ', @col_names, ')');

SET @query = CONCAT('(SELECT * FROM ', @table_schema, '.', @table_name,
  ' INTO OUTFILE \'/tmp/your_csv_file.csv\'
  FIELDS ENCLOSED BY \'\\\'\' TERMINATED BY \'\t\' ESCAPED BY \'\'
  LINES TERMINATED BY \'\n\')');

/* Concatenates column names to query */
SET @sql = CONCAT(@cols, ' UNION ALL ', @query);

/* Resets Group Contact Max Limit back to original value */
SET SESSION group_concat_max_len = @default_group_concat_max_len;

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

1
ฉันควรพูดวิธีแก้ปัญหา นำมาใช้เพื่อวัตถุประสงค์ของฉัน ขอบคุณ!
Denis Kulagin

0

ฉันต้องการเพิ่มคำตอบที่ Sangam Belose ให้ไว้ นี่คือรหัสของเขา:

select ('id') as id, ('time') as time, ('unit') as unit
UNION ALL
SELECT * INTO OUTFILE 'C:/Users/User/Downloads/data.csv'
  FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
  LINES TERMINATED BY '\n'
  FROM sensor

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

SHOW VARIABLES LIKE "secure_file_priv"

ผลลัพธ์ควรมีลักษณะดังนี้:

mysql> show variables like "%secure_file_priv%";
+------------------+------------------------------------------------+
| Variable_name    | Value                                          |
+------------------+------------------------------------------------+
| secure_file_priv | C:\ProgramData\MySQL\MySQL Server 8.0\Uploads\ |
+------------------+------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

คุณสามารถเปลี่ยนตัวแปรนี้หรือเปลี่ยนแบบสอบถามเพื่อส่งออกไฟล์ไปยังเส้นทางเริ่มต้นที่แสดง


0

MySQL เพียงอย่างเดียวไม่เพียงพอที่จะทำเพียงแค่นี้ ด้านล่างนี้เป็นสคริปต์ PHP ที่จะส่งออกคอลัมน์และข้อมูลเป็น CSV

ป้อนชื่อฐานข้อมูลและตารางใกล้ด้านบน

<?php

set_time_limit( 24192000 );
ini_set( 'memory_limit', '-1' );
setlocale( LC_CTYPE, 'en_US.UTF-8' );
mb_regex_encoding( 'UTF-8' );

$dbn = 'DB_NAME';
$tbls = array(
'TABLE1',
'TABLE2',
'TABLE3'
);

$db = new PDO( 'mysql:host=localhost;dbname=' . $dbn . ';charset=UTF8', 'root', 'pass' );

foreach( $tbls as $tbl )
{
    echo $tbl . "\n";
    $path = '/var/lib/mysql/' . $tbl . '.csv';

    $colStr = '';
    $cols = $db->query( 'SELECT COLUMN_NAME AS `column` FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = "' . $tbl . '" AND TABLE_SCHEMA = "' . $dbn . '"' )->fetchAll( PDO::FETCH_COLUMN );
    foreach( $cols as $col )
    {
        if( $colStr ) $colStr .= ', ';
        $colStr .= '"' . $col . '"';
    }

    $db->query(
    'SELECT *
    FROM
    (
        SELECT ' . $colStr . '
        UNION ALL
        SELECT * FROM ' . $tbl . '
    ) AS sub
    INTO OUTFILE "' . $path . '"
    FIELDS TERMINATED BY ","
    ENCLOSED BY "\""
    LINES TERMINATED BY "\n"'
    );

    exec( 'gzip ' . $path );

    print_r( $db->errorInfo() );
}

?>

คุณจะต้องเป็นไดเร็กทอรีที่คุณต้องการส่งออกไป MySQL ต้องมีความสามารถในการเขียนลงในไดเร็กทอรี

$path = '/var/lib/mysql/' . $tbl . '.csv';

คุณสามารถแก้ไขตัวเลือกการส่งออก CSV ในแบบสอบถาม:

INTO OUTFILE "' . $path . '"
FIELDS TERMINATED BY ","
ENCLOSED BY "\""
LINES TERMINATED BY "\n"'

ในตอนท้ายมีการเรียกผู้บริหารไปที่ GZip CSV


-1
เลือก 'ColName1', 'ColName2', 'ColName3'
ยูเนี่ยนทั้งหมด
เลือก ColName1, ColName2, ColName3
    จาก YourTable
    เข้าสู่ OUTFILE 'c: \\ datasheet.csv' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '' 'LINES TERMINATED BY' \ n ' 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.