การใช้นามแฝงคอลัมน์ในส่วนคำสั่ง WHERE ของแบบสอบถาม MySQL สร้างข้อผิดพลาด


201

ข้อความค้นหาที่ฉันเรียกใช้มีดังนี้ แต่ฉันได้รับข้อผิดพลาดนี้:

# 1054 - Unknown คอลัมน์ 'guaranteed_postcode' ใน 'IN / ทั้งหมด / ใดแบบสอบถามย่อย'

SELECT `users`.`first_name`, `users`.`last_name`, `users`.`email`,
SUBSTRING(`locations`.`raw`,-6,4) AS `guaranteed_postcode`
FROM `users` LEFT OUTER JOIN `locations`
ON `users`.`id` = `locations`.`user_id`
WHERE `guaranteed_postcode` NOT IN #this is where the fake col is being used
(
 SELECT `postcode` FROM `postcodes` WHERE `region` IN
 (
  'australia'
 )
)

คำถามของฉันคือ: เหตุใดฉันจึงไม่สามารถใช้คอลัมน์ปลอมในส่วนคำสั่ง DB เดียวกัน

คำตอบ:


434

คุณสามารถใช้นามแฝงคอลัมน์ในกลุ่มตาม, ตามลำดับหรือคำสั่งที่มี

SQL มาตรฐานไม่อนุญาตให้คุณอ้างอิงนามแฝงคอลัมน์ในส่วนคำสั่ง WHERE ข้อ จำกัด นี้ถูกกำหนดเพราะเมื่อมีการใช้รหัส WHERE ค่าคอลัมน์อาจยังไม่ได้รับการพิจารณา

คัดลอกมาจากเอกสาร MySQL

ตามที่ระบุไว้ในความคิดเห็นการใช้ HAVING แทนอาจใช้งานได้ ตรวจสอบให้แน่ใจว่าได้อ่านที่WHERE vs HAVINGนี้


1
ไชโยสำหรับการตอบสนองที่รวดเร็วและแม่นยำ! ฉันได้ดูในส่วนคำสั่ง HAVING และหาวิธีเรียกใช้แบบสอบถามนี้ให้สำเร็จ ขอบคุณอีกครั้ง.
James

38
ในกรณีที่คนอื่นมีปัญหาเช่นเดียวกับฉันซึ่งใช้คอลัมน์นามแฝงในข้อที่ล้มเหลว - การสลับ 'WHERE' เป็น 'การได้รับการแก้ไขคงได้ทันที +1 คำตอบที่ดี
megaSteve4

@ megaSteve4 ฉันมีปัญหาเดียวกัน! การใช้ "HAVING" สามารถแก้ไขได้อย่างราบรื่น :)
Johan

9
นี่อาจเป็นหรือไม่สำคัญในกรณีของคุณ แต่HAVINGดำเนินการช้ากว่าWHERE
DTs

1
เหตุผลที่ผลงานเป็นเพราะค่าของคอลัมน์จะต้องมีการคำนวณตามเวลาที่คุณจะได้รับการhaving havingนี่ไม่ใช่กรณีด้วยwhereตามที่ระบุไว้ข้างต้น
Millie Smith

24

ดังที่วิกเตอร์ชี้ให้เห็นปัญหานี้เกิดจากนามแฝง สิ่งนี้สามารถหลีกเลี่ยงได้โดยการใส่นิพจน์ลงในคำสั่ง WHERE x IN y โดยตรง:

SELECT `users`.`first_name`,`users`.`last_name`,`users`.`email`,SUBSTRING(`locations`.`raw`,-6,4) AS `guaranteed_postcode`
FROM `users` LEFT OUTER JOIN `locations`
ON `users`.`id` = `locations`.`user_id`
WHERE SUBSTRING(`locations`.`raw`,-6,4) NOT IN #this is where the fake col is being used
(
 SELECT `postcode` FROM `postcodes` WHERE `region` IN
 (
  'australia'
 )
)

อย่างไรก็ตามฉันเดาว่านี่จะไม่มีประสิทธิภาพมากเนื่องจากแบบสอบถามย่อยจะต้องดำเนินการสำหรับทุกแถวของแบบสอบถามภายนอก


1
@rodion ใช่ผมเชื่อว่านี่เป็นอย่างรุนแรงช้าและไม่มีประสิทธิภาพ
Pacerier

20

SQL มาตรฐาน (หรือ MySQL) ไม่อนุญาตให้ใช้ชื่อแทนคอลัมน์ใน WHERE clause เนื่องจาก

เมื่อคำสั่ง WHERE ถูกประเมินค่าของคอลัมน์อาจยังไม่ได้รับการพิจารณา

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

SELECT `users`.`first_name`, `users`.`last_name`, `users`.`email`,
@postcode AS `guaranteed_postcode`
FROM `users` LEFT OUTER JOIN `locations`
ON `users`.`id` = `locations`.`user_id`
WHERE (@postcode := SUBSTRING(`locations`.`raw`,-6,4)) NOT IN
(
 SELECT `postcode` FROM `postcodes` WHERE `region` IN
 (
  'australia'
 )
)

วิธีนี้จะหลีกเลี่ยงการทำซ้ำนิพจน์เมื่อมันเพิ่มความซับซ้อนทำให้การบำรุงรักษาโค้ดง่ายขึ้น


9
สิ่งนี้ไม่ขัดแย้งกับเอกสารที่ระบุว่า "ตามกฎทั่วไปคุณไม่ควรกำหนดค่าให้กับตัวแปรผู้ใช้และอ่านค่าภายในคำสั่งเดียวกันคุณอาจได้รับผลลัพธ์ตามที่คาดหวัง แต่สิ่งนี้ไม่รับประกัน" ?
Arjan

นั่นเป็นสิ่งที่ต้องจำไว้อย่างแน่นอน มันใช้งานได้สำหรับฉันเสมอฉันคิดว่าลำดับการประเมินของส่วนต่าง ๆ ของคำสั่งต้องได้รับการแก้ไข (ก่อนจากที่ใดก็ได้จากนั้นเลือกจากนั้นเลือกกลุ่มตาม ... ) แต่ฉันไม่มีการอ้างอิงสำหรับสิ่งนั้น
Joni

ตัวอย่างเล็ก ๆ น้อย ๆ : บางคนอ้างว่าสำหรับพวกเขาselect @code:=sum(2), 2*@codeทำงานใน MySQL 5.5 แต่สำหรับฉันใน 5.6 คอลัมน์ที่สองให้ผลเป็น NULL ในการเรียกใช้ครั้งแรกและส่งกลับ 2 เท่าของผลลัพธ์ก่อนหน้าเมื่อทำงานอีกครั้ง น่าสนใจพอทั้งเลือก@code:=2, 2*@codeและselect @code:=rand(), 2*@codeดูเหมือนว่าจะทำงานใน 5.6 ของฉัน (วันนี้) แต่สิ่งเหล่านี้เป็นการเขียนและการอ่านในประโยค SELECT ในกรณีของคุณคุณกำลังตั้งค่าไว้ที่ใด
Arjan

@Joni ทำไมไม่เพียงแค่ประเมินเงื่อนไขสองครั้ง แน่นอนว่า MySQL นั้นฉลาดพอที่จะปรับให้เหมาะสม .......
Pacerier

@Pierier ต้องทำซ้ำการแสดงออกยังคงแย่ลงโดยเฉพาะถ้ามันซับซ้อน ฉันไม่สามารถยืนยันได้ว่า MySQL ใช้การกำจัดคำสั่งย่อยทั่วไปหรือไม่
Joni

16

บางทีคำตอบของฉันอาจจะสายเกินไป แต่สิ่งนี้สามารถช่วยเหลือผู้อื่นได้

คุณสามารถใส่คำสั่ง select อื่นลงไปและใช้คำสั่งย่อย

SELECT * FROM (Select col1, col2,...) as t WHERE t.calcAlias > 0

calcAlias ​​เป็นคอลัมน์นามแฝงที่คำนวณ


ดีและสั้น แต่มันก็คลุมเครือเกินกว่าจะเป็นประโยชน์ได้
Agamemnus

@Agamemnus คุณหมายถึงอะไร?
Pacerier

คำถามคือ "ทำไมฉันไม่สามารถใช้คอลัมน์ปลอมในส่วนคำสั่งฐานข้อมูลแบบสอบถามเดียวกันได้ที่ไหน" คำตอบนี้ไม่ตอบคำถามนั้นและไม่มีคำกริยา
Agamemnus

จากนั้นใช้ HAVING
Hett

8

คุณสามารถใช้ HAVING clause สำหรับตัวกรองที่คำนวณในเขตข้อมูล SELECT และ aliases


@ fahimg23 - ไม่แน่ใจ ฉันพยายามหาเหตุผลว่าทำไม แต่ฉันทำไม่ได้! โปรดจำไว้ว่าความแตกต่างระหว่างWHEREและHAVINGแม้ว่า พวกมันไม่เหมือนกัน stackoverflow.com/search?q=where+vs+having
rinogo

UPDATE: เป็นเพราะคำตอบนี้ให้วิธีการแก้ปัญหาเดียวกัน แต่มีข้อมูลเพิ่มเติม
rinogo

1

ฉันใช้ mysql 5.5.24 และใช้งานรหัสต่อไปนี้:

select * from (
SELECT `users`.`first_name`, `users`.`last_name`, `users`.`email`,
SUBSTRING(`locations`.`raw`,-6,4) AS `guaranteed_postcode`
FROM `users` LEFT OUTER JOIN `locations`
ON `users`.`id` = `locations`.`user_id`
) as a
WHERE guaranteed_postcode NOT IN --this is where the fake col is being used
(
 SELECT `postcode` FROM `postcodes` WHERE `region` IN
 (
  'australia'
 )
)

0

SQL มาตรฐานไม่อนุญาตให้อ้างอิงถึงชื่อแทนคอลัมน์ในส่วนคำสั่ง WHERE ข้อ จำกัด นี้ถูกกำหนดเพราะเมื่อมีการประเมินส่วนคำสั่ง WHERE ค่าคอลัมน์อาจยังไม่ได้รับการพิจารณา ตัวอย่างเช่นแบบสอบถามต่อไปนี้ผิดกฎหมาย:

เลือก id, COUNT (*) เป็น cnt จาก tbl_name WHERE cnt> 0 GROUP BY id;


0

คุณสามารถใช้ SUBSTRING ( locations. raw, -6,4 ) สำหรับตำแหน่งที่ conditon

SELECT `users`.`first_name`, `users`.`last_name`, `users`.`email`,
SUBSTRING(`locations`.`raw`,-6,4) AS `guaranteed_postcode`
FROM `users` LEFT OUTER JOIN `locations`
ON `users`.`id` = `locations`.`user_id`
WHERE SUBSTRING(`locations`.`raw`,-6,4) NOT IN #this is where the fake col is being used
(
SELECT `postcode` FROM `postcodes` WHERE `region` IN
(
 'australia'
)
)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.