วิธีการสร้างแบบสอบถามย่อยโดยใช้ JDatabase


31

ที่http://docs.joomla.org/Selecting_data_using_JDatabaseไม่มีวิธีที่เป็นเอกสารสำหรับการเขียนแบบสอบถามย่อยโดยใช้ JDatabase

https://gist.github.com/gunjanpatel/8663333เป็นตัวอย่างวิธีหนึ่งในการทำสิ่งนี้ให้สำเร็จด้วย (ละเว้นบิตไม่กี่):

$subQuery = $db->getQuery(true);
$query    = $db->getQuery(true);

// Create the base subQuery select statement.
$subQuery->select('*')
    ->from($db->quoteName('#__sub_table'))
    ->where($db->quoteName('subTest') . ' = ' . $db->quote('1'));

// Create the base select statement.
$query->select('*')
    ->from($db->quoteName('#__table'))
    ->where($db->quoteName('state') . ' = ' . $db->quote('1'))
    ->where($db->quoteName('subCheckIn') . ' IN (' . $subQuery->__toString() . ')')
    ->order($db->quoteName('ordering') . ' ASC');

// Set the query and load the result.
$db->setQuery($query);

ดูเหมือนว่าวิธีการที่ดีและเป็นไปได้ แต่มีวิธีที่ดีกว่าหรือไม่


4
คุณสามารถละเว้นการเรียก toString () บน $ subQuery Joomla! จะจัดการให้คุณโดยอัตโนมัติ นอกจากนั้นฉันใช้วิธีเดียวกันนี้และมันก็ใช้ได้ดีสำหรับฉัน
Zachary Draper

เป็นวิธีเดียวกันกับที่เราใช้ใน com_content ใน core github.com/joomla/joomla-cms/blob/staging/components/ …
George Wilson

@ZacharyDraper ที่น่าสนใจ คุณสามารถแสดงรหัสที่รับผิดชอบได้หรือไม่?
Dmitry Rekun

3
@ ZacharyDraper: PHP (แทนที่จะเป็น Joomla! per se) จัดการให้คุณ ( __toString()) เป็นวิธีการ "วิเศษ"
MrWhite

ใช่ขอบคุณ w3d
Zachary Draper

คำตอบ:


16

ใช่เท่าที่ฉันกังวลวิธีการสร้างแบบสอบถามย่อยนั้นเป็นวิธีที่ผู้พัฒนาส่วนขยายของ joomla ส่วนใหญ่นำมาใช้

ฉันใช้วิธีเดียวกันนี้กับส่วนขยายและส่วนขยายที่กำหนดเองบางรายการสำหรับลูกค้า

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


10

AFAIK ไม่มีวิธีการสร้างแบบสอบถามย่อยที่ง่ายซึ่งอาจเป็นข้อบกพร่องในระบบและควรแก้ไขผ่าน PR

อย่างไรก็ตามฉันไม่เห็นปัญหากับตัวอย่างของคุณ - ดูเหมือนสมเหตุสมผลพอสมควร

~~~

นี่คือตัวอย่างในการตอบสนองต่อความคิดเห็นของ @ DavidFritsch ด้านล่าง ยิ่งฉันคิดถึงมันมากเท่าไรฉันยิ่งชอบวิธีการที่เรียบง่ายมากขึ้นที่แสดงใน OP มันชัดเจนยิ่งขึ้นว่าเกิดอะไรขึ้น

$query = $this->db->getQuery(true)
  ->select('a.*')
  ->subQuery()
    ->select('b.*')
    ->from('#__table_b AS b')
    ->as('subQueryResult')
  ->endSubQuery()
  ->from('#__table_a AS a');

1
คุณมีความคิดใด ๆ ว่าสิ่งนี้สามารถทำงานได้อย่างไร? ฉันพยายามจินตนาการถึงรูปแบบที่คุณจะใช้เพื่อทำให้การทำงานกับวัตถุแบบสอบถามหนึ่งและไม่มีอะไรรู้สึกง่ายกว่าวิธีนี้
David Fritsch

1
มันอาจจะคุ้มค่าที่จะสร้างsubQuerySelectวิธีการที่ทำให้คุณสามารถทำได้มากกว่า "หมดจด" ฉันจะแก้ไขคำตอบของฉันเพื่อให้และเป็นตัวอย่าง
Don Gilbert

ฉันชอบที่จะเห็นว่าใน Joomla
fruppel

3

นอกจากนี้ยังมีวิธีเรียกใช้คิวรีที่มีเคียวรีย่อยโดยใช้ Joomla Platform API แนวคิดพื้นฐานเกี่ยวกับวิธีการใช้ subqueries จะขึ้นอยู่กับgunjanpatel

นี่คือตัวอย่างสำหรับการดำเนินการค้นหาในNested Set Models :

แบบสอบถาม SQL:

-- Find the Immediate Subordinates of a Node
SELECT node.title, (COUNT(parent.id) - (sub_tree.depth + 1)) AS depth
FROM lubd3_usergroups AS node,
        lubd3_usergroups AS parent,
        lubd3_usergroups AS sub_parent,
        (
                SELECT node.id, (COUNT(parent.id) - 1) AS depth
                FROM lubd3_usergroups AS node,
                        lubd3_usergroups AS parent
                WHERE node.lft BETWEEN parent.lft AND parent.rgt
                        AND node.id = 1
                GROUP BY node.id
                ORDER BY node.lft
        )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
        AND sub_parent.id = sub_tree.id
GROUP BY node.id
-- not showing the parent node
HAVING depth = 1
-- showing the parent node
-- HAVING depth <= 1
ORDER BY node.lft;

และการสืบค้นที่แปลงแล้วจะถูกดำเนินการโดย Joomla:

// Create the subQuery select statement.
// Nested Set Queries: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
// CROSS JOIN: http://www.informit.com/articles/article.aspx?p=30875&seqNum=5
$subQuery->select(array('node.id', '(COUNT(parent.id) - 1) AS depth'))
    ->from($db->quoteName('#__usergroups') . 'node')
    ->join('CROSS', $db->quoteName('#__usergroups', 'parent'))
    ->where($db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('parent.lft') . ' AND ' . $db->quoteName('parent.rgt') . ' AND ' . $db->quoteName('node.id') . ' = ' . $db->quote('1'))
    ->group($db->quoteName('node.id'))
    ->order($db->quoteName('node.lft'));

// Create the base select statement.
$query->select(array('node.title', '(COUNT(parent.id) - (sub_tree.depth + 1)) AS depth'))
    ->from($db->quoteName('#__usergroups') . 'node')
    ->join('CROSS', $db->quoteName('#__usergroups', 'parent'))
    ->join('CROSS', $db->quoteName('#__usergroups', 'sub_parent'))
    ->join('CROSS', '(' . $subQuery .') AS sub_tree')
    ->where($db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('parent.lft') . ' AND ' . $db->quoteName('parent.rgt')
    . ' AND ' . $db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('sub_parent.lft') . ' AND ' . $db->quoteName('sub_parent.rgt')
    . ' AND ' . $db->quoteName('sub_parent.id') . ' = ' . $db->quoteName('sub_tree.id'))
    ->group($db->quoteName('node.id'))
    ->having($db->quoteName('depth') . ' = ' . $db->quote('1'))
    ->order($db->quoteName('node.lft'));

// Set the query and load the result.
$db->setQuery($query);
$rowList = $db->loadAssocList();

echo "<pre>";
print_r($rowList);
echo "</pre>";

1
ดูดี แต่เป็นแบบเดียวกับในตัวอย่างของ OP: ทำแบบสอบถามย่อยก่อนและใช้มันในแบบสอบถามหลัก คำถามคือถ้ามีวิธีที่ดีกว่า
fruppel

1

ฉันจะเสนอตัวอย่างข้อมูลรุ่นของฉันจากนั้นอธิบายเหตุผลของฉันและรวมราคาจากคู่มือมาตรฐานการเข้ารหัสของ Joomla (ซึ่งจะจัดรูปแบบอัญประกาศ)

$subquery = $db->getQuery(true)
    ->select("checkin")
    ->from("#__sub_table")
    ->where("subTest = 1");

$query = $db->getQuery(true)
    ->select("*")
    ->from("#__table")
    ->where([
        "state = 1",
        "subCheckIn IN ({$subQuery})"
    ])
    ->order("ordering");

$db->setQuery($query);

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

  • ฉันเขียนข้อความค้นหาที่อยู่ลึกที่สุดก่อนและดำเนินการกับแบบสอบถามที่อยู่นอกสุด สิ่งนี้ทำให้ฉันเชื่อมโยงทุกวิธีการสร้างแบบสอบถามโดยตรงกับgetQuery()วิธีการ อย่างมีประสิทธิภาพชื่อตัวแปรจะถูกเขียนเพียงครั้งเดียวในขณะที่สร้างแบบสอบถามแต่ละรายการ
    ต่อไปนี้เป็นตัวอย่างที่ยอดเยี่ยมของการซ้อนการสืบค้นจำนวนมาก (เมื่อฉันคิดว่ามันน่ารักที่จะจัดเรียงลูกศรผูกสายไว้)

  • ฉันพยายามที่จะหลีกเลี่ยงการทำหลายselect()และ / หรือwhere()การโทรภายในแบบสอบถามเดียวกันเพราะผมได้เห็นมันนำไปสู่ความสับสนของนักพัฒนาที่มีประสบการณ์น้อย เนื่องจากวิธีการเหล่านี้ยอมรับอาร์เรย์ฉันคิดว่ามันอ่านได้ง่ายกว่า

  • และในที่สุดก็เป็นหัวข้อที่ถกเถียงกันมากที่สุด ...

    ชื่อตารางและชื่อคอลัมน์ตารางควรอยู่ในเมธอด quoteName () เสมอเพื่อหลีกเลี่ยงชื่อตารางและคอลัมน์ตาราง ค่าฟิลด์ที่ตรวจสอบในแบบสอบถามควรอยู่ในเมธอด quote () เสมอเพื่อหลีกเลี่ยงค่าก่อนส่งผ่านไปยังฐานข้อมูล ค่าเขตข้อมูลจำนวนเต็มที่ตรวจสอบในแบบสอบถามควรเป็นประเภทการส่งไปยัง (int)

    ฉันขัดแย้งกับท่าทางนี้มาก เมื่อฉันมา Joomla ครั้งแรกเมื่อปีที่แล้วฉันคิดว่าฉันจะไม่โทรหาไร้ประโยชน์ (ไม่มีประโยชน์ต่อความเสถียรความปลอดภัยความสามารถในการอ่านข้อมูลของแบบสอบถาม) จากค่าคงที่! แต่นายจ้างของฉันชอบความคิดของ toeing สาย Joomla และผมต้องยอมรับว่าผมมักมีความชื่นชมสูงสำหรับกฎระเบียบเพื่อให้ฉันได้รับการฉีดลงคำสั่งของฉันด้วยquote(), (int)และquoteName()ซึ่งยังหมายถึงกองสตริง (ทั้งหมด เว้นระยะห่างอย่างเหมาะสม) ผลลัพธ์สุดท้ายของการทำงานของฉันคือบล็อกข้อความค้นหาที่ป่องอย่างน่ากลัวซึ่งแม้ฉันจะรู้สึกลำบากใจ บรรทัดที่แย่ที่สุด / ยาวที่สุดที่ไม่ให้ยืมตัวเองในแนวดิ่งคือการjoin()เรียกเนื่องจาก tablename, alias ON, จากนั้นเงื่อนไขอย่างน้อยหนึ่งข้อที่อาจหรือไม่จำเป็นต้องมีการอ้างอิงฉันรู้สึกซาบซึ้งที่นโยบายนี้ถูกนำมาใช้โดยคำนึงถึงความปลอดภัยสำหรับนักพัฒนามือใหม่ แต่ฉันแน่ใจว่าต้องการหากนโยบายนี้มีความรู้สึกอ่อนไหวว่าผู้เขียนโค้ด Joomla ทุกคนนั้นไม่ใช่นักลอกเลียนแบบที่ไม่รู้ตัว ฉันหมายถึงลองดูว่าโค้ดดูสะอาดและย่อโดยไม่จำเป็น

  • สำหรับการถู:

    • ฉันแทบไม่เคยใช้*คำสั่ง SELECT ของฉันเลย
    • ฉันไม่เคยโทร __toString()
    • ฉันไม่ได้อ้างถึงจำนวนเต็มฉันโยนมันเป็นจำนวนเต็ม
    • ฉันไม่ได้เขียนASCเพราะนั่นคือทิศทางการเรียงลำดับเริ่มต้น
    • ฉันพยายามอย่างเต็มที่ที่จะไม่ใช้คำสำคัญ mysql เมื่อสร้างชื่อตารางและชื่อคอลัมน์ใหม่
    • เป็นเรื่องของการตั้งค่าส่วนตัวฉันมักจะใช้การอ้างสองครั้งในการโต้แย้งสตริงวิธีการของฉันเพื่อรักษาความสม่ำเสมอแยกความแตกต่างจากการอ้างเดี่ยวของ mysql และเพื่อให้ฉันสามารถเพลิดเพลินกับการแก้ไขตัวแปรที่ฉันเขียนทั่วไปด้วย " ซับซ้อนไวยากรณ์ "
    • ฉันใช้ชื่อตัวแปรข้อมูลและการแสดงความคิดเห็นเพื่อช่วยในการอ่านข้อความค้นหาซ้อนของฉันและรหัสของฉันโดยทั่วไป
    • ฉันทดสอบรหัสก่อนที่จะออกจากการดูแลของฉัน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.