Metode untuk membuat subquery menggunakan JDatabase


31

Di http://docs.joomla.org/Selecting_data_using_JDatabase , tidak ada metode yang terdokumentasi untuk menulis subquery menggunakan JDatabase.

https://gist.github.com/gunjanpatel/8663333 mencontohkan satu cara untuk mencapai ini dengan (beberapa bit dihilangkan):

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

Ini sepertinya pendekatan yang bagus dan masuk akal, tetapi adakah yang lebih baik?


4
Anda dapat menghilangkan panggilan toString () di $ subQuery. Joomla! secara otomatis akan menanganinya untuk Anda. Selain itu, saya menggunakan metode yang sama dan ini bekerja dengan baik untuk saya.
Zachary Draper

Ini juga metode yang sama yang kami gunakan di com_content di core github.com/joomla/joomla-cms/blob/staging/components/…
George Wilson

@ZacharyDraper menarik. Bisakah Anda menunjukkan kode yang bertanggung jawab untuk itu?
Dmitry Rekun

3
@ZacharyDraper: PHP (daripada Joomla! Per se) menanganinya untuk Anda ( __toString()) adalah metode "ajaib".
MrWhite

Ya, terima kasih w3d.
Zachary Draper

Jawaban:


16

Ya, sejauh yang saya ketahui, cara Anda membangun subquery adalah yang diadopsi oleh mayoritas pengembang ekstensi joomla.

Saya menggunakan metode yang sama pada beberapa ekstensi saya dan ekstensi khusus yang dibuat untuk klien.

Tidak ada cara "resmi" untuk melakukan ini, tetapi melakukannya seperti yang Anda tunjukkan memungkinkan Anda menggunakan pembuat kueri dan masih mempertahankan jumlah keterbacaan yang baik


10

AFAIK tidak ada cara bawaan untuk melakukan subkueri mudah, yang mungkin merupakan kekurangan dalam sistem dan harus diperbaiki melalui PR.

Namun, saya tidak melihat masalah dengan contoh Anda - tampaknya cukup masuk akal.

~~~

Berikut adalah contoh dalam menanggapi komentar @ DavidFritsch di bawah ini. Semakin saya memikirkannya, semakin baik saya menyukai pendekatan yang lebih sederhana yang ditampilkan di OP. Lebih jelas apa yang terjadi.

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

1
Apakah Anda tahu bagaimana ini bisa dibuat bekerja? Saya mencoba membayangkan format yang akan Anda gunakan untuk menjadikan ini berfungsi pada satu objek permintaan dan sebenarnya tidak ada yang terasa lebih mudah daripada metode ini.
David Fritsch

1
Mungkin bermanfaat untuk membuat subQuerySelectmetode yang memungkinkan Anda melakukannya sedikit lebih "bersih". Saya akan mengedit jawaban saya untuk memberikan dan memberi contoh.
Don Gilbert

Saya akan senang melihatnya di Joomla
fruppel

3

Ada juga cara untuk mengeksekusi kueri yang berisi subkueri menggunakan Joomla Platform API. Gagasan dasar tentang cara menggunakan subqueries didasarkan pada gunjanpatel .

Berikut ini adalah contoh untuk menjalankan kueri pada Nested Set Models :

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

dan kueri yang diubah untuk dieksekusi oleh 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
Terlihat bagus tetapi benar-benar sama seperti pada contoh OP: Buat subquery terlebih dahulu dan gunakan kemudian di kueri utama. Pertanyaannya adalah apakah ada cara yang lebih baik.
fruppel

1

Saya akan menawarkan versi snippet saya kemudian menjelaskan justifikasi saya dan menyertakan kutipan dari manual Standar Coding Joomla (yang akan diformat dengan kunci kueblock).

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

Gunakan rantai kueri untuk menghubungkan sejumlah metode kueri, satu demi satu, dengan setiap metode mengembalikan objek yang dapat mendukung metode berikutnya, ini meningkatkan keterbacaan dan menyederhanakan kode yang dihasilkan.

  • Saya menulis kueri paling dalam terlebih dahulu dan maju ke kueri terluar. Ini memungkinkan saya rantai semua metode pembuatan kueri langsung ke getQuery()metode. Secara efektif, nama variabel hanya ditulis sekali saat membangun permintaan individu.
    Berikut ini adalah contoh hebat dari beberapa permintaan bersarang yang berat (ketika saya pikir itu lucu untuk memasang panah rantai).

  • Saya mencoba untuk menghindari membuat beberapa select()dan / atau where()panggilan dalam permintaan yang sama karena saya telah melihatnya mengarah pada kebingungan pengembang yang kurang berpengalaman . Karena metode ini menerima array, saya merasa lebih mudah dibaca dan praktik pengkodean yang lebih baik untuk menggunakannya.

  • dan akhirnya topik yang paling kontroversial ...

    Nama tabel dan nama kolom tabel harus selalu dilampirkan dalam metode quoteName () untuk menghindari nama tabel dan kolom tabel. Nilai bidang yang diperiksa dalam kueri harus selalu dilampirkan dalam metode quote () untuk menghindari nilai sebelum meneruskannya ke database. Nilai-nilai bidang integer yang diperiksa dalam kueri juga harus diketikkan ke (int).

    Saya sangat bertentangan dengan pendirian ini. Ketika saya pertama kali datang ke Joomla tahun lalu, saya pikir, saya tidak akan membuat panggilan yang tidak berguna (tidak ada manfaat untuk stabilitas, keamanan, keterbacaan permintaan) pada nilai statis! Namun, majikan saya menyukai gagasan tengah menuruti Joomla, dan saya harus mengakui bahwa saya umumnya memiliki apresiasi yang tinggi untuk aturan, jadi saya telah menyiram turun pertanyaan saya dengan quote(), (int), dan quoteName()yang juga berarti tumpukan string concatenation (semua ditempatkan dengan benar). Hasil akhir dari pekerjaan saya adalah blok permintaan yang membengkak secara mengerikan yang bahkan saya kesulitan melihatnya. Baris terburuk / terpanjang yang tidak cocok untuk penumpukan vertikal adalah join()panggilan karena nama tab, alias ON, maka satu atau lebih kondisi yang mungkin atau mungkin tidak memerlukan kuotasi.Saya bisa menghargai bahwa kebijakan ini diterapkan dengan mempertimbangkan keamanan untuk pengembang pemula, tetapi saya yakin akan suka jika kebijakan ini entah bagaimana marah dengan sensibilitas bahwa tidak semua coders Joomla adalah copy-pastor yang bodoh. Maksudku, lihat bagaimana bersih dan singkat kode terlihat tanpa panggilan yang tidak perlu.

  • Adapun pembersihan:

    • Saya hampir tidak pernah menggunakan *klausa SELECT saya
    • Saya tidak pernah menelepon __toString()
    • Saya tidak mengutip bilangan bulat, saya menyebutnya bilangan bulat
    • Saya tidak menulis ASCkarena itu adalah arah penyortiran default
    • Saya berusaha keras untuk tidak menggunakan kata kunci mysql saat membuat nama tabel dan nama kolom baru
    • Sebagai preferensi pribadi, saya cenderung menggunakan kutip ganda pada argumen string metode saya untuk menjaga keseragaman, membedakan dari kutip tunggal mysql, dan agar saya dapat menikmati interpolasi variabel yang biasanya saya tulis dengan " sintaksis kompleks ".
    • Saya menggunakan nama variabel informatif dan berkomentar untuk membantu keterbacaan kueri saya yang bersarang, dan kode saya secara umum
    • Saya menguji kode saya sebelum meninggalkan hak asuh saya
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.