In MOODLE, I'm using the following script to pull some numbers -- and they come out fine:
SELECT
qc.name,
q.category,
SUM(IF(qs.grade = "1",1,0)) AS Correct,
SUM(IF(qs.grade = "0",1,0)) AS Wrong
FROM
mdl_question_states qs,
mdl_quiz_attempts qa,
mdl_quiz qz,
mdl_course c,
mdl_question q,
mdl_question_categories qc
WHERE
qa.id = qs.attempt
AND qs.event = 6
AND qa.quiz = qz.id
AND ((qz.name = 'Pre-Test') OR (qz.name = 'Post-Test'))
AND qz.course = c.id
AND q.id = qs.question
AND q.category = qc.id
AND q.category > 601
GROUP BY q.category
ORDER BY qc.name
My question is this: I want to have a column (after the 'SUM(IF...' columns) that are 'Correct -AND- Pre-Test' followed by 'Correct -AND- Post-Test'.
What is the syntax to use to accomplish this?
Try
SUM(IF(sq.grade = '1' AND qz.name='pre-test', 1, 0)) AS cor_and_pre
and similarly for the other value.
Related
When I add the nested query for invCount, my query time goes from .03 sec to 14 sec. The query works and I get correct values, but it is very, very slow in comparison. Is that just because I have to many conditions in that query? When I take it out and still have the second nested query, the time is still .03 secs. There is clearly something about the first nested query the database doesn't like, but I am not seeing what it is. I have a foreign key set for all the inner join lines too. Any help or ideas would be appreciated.
SELECT a.*,
f.name,
f.partNumber,
f.showInAdminStore,
f.showInPublicStore,
f.productImage,
r.mastCatID,
(SELECT COUNT(b.inventoryID)
FROM storeInventory b
INNER JOIN events c ON c.eventID = b.eventID
WHERE b.pluID = a.pluID
AND b.listPrice = a.listPrice
AND b.unlimitedQty = a.unlimitedQty
AND (b.packageID = a.packageID OR (b.packageID IS NULL AND a.packageID IS NULL))
AND b.orderID IS NULL
AND c.isOpen = '1'
AND b.paymentTypeID <= '2'
AND (b.inCart < '$cartTime' OR b.inCart IS NULL) ) AS invCount,
(SELECT COUNT(x.inventoryID)
FROM storeInventory x
WHERE x.packageID = a.inventoryID) AS packageCount
FROM storeInventory a
INNER JOIN storePLUs f ON f.pluID = a.pluID
INNER JOIN storeCategories r ON r.catID = f.catID
INNER JOIN events d ON d.eventID = a.eventID
WHERE a.storeFrontID = '1'
AND a.orderID IS NULL
AND a.paymentTypeID <= '2'
AND d.isOpen = '1'
GROUP BY a.packageID, a.unlimitedQty, a.listPrice, a.pluID
Table from query output
UPDATE: 12/12/2022
I changed the line checking the packageID to "AND (b.packageID <=> a.packageID)" as suggested and that cut my query time down to 7.8 seconds from 14 seconds. Thanks for the pointer. I will definitely use that in the future for NULL comparisons.
using "count(*)" took about half a second off. When I take the first nested query out, it drops down to .05 seconds even with the other nested queries in there, so I feel like there is still something causing issues. I tried running it without the other "AND (b.inCart < '$cartTime' OR b.inCart IS NULL)" line and that did take about a second off, but no where what I was hoping for. Is there an operand that includes NULL on a less than comparison? I also tried running it without the inner join in the nested query and that didn't change much at all. Of course removing any of that, throughs the values off and they become incorrect, so I can't run it that way.
Here is my current query setup that still pulls correct values.
SELECT a.*,
f.name,
f.partNumber,
f.showInAdminStore,
f.showInPublicStore,
f.productImage,
r.mastCatID,
(SELECT COUNT(*)
FROM storeInventory b
INNER JOIN events c ON c.eventID = b.eventID
WHERE b.pluID = a.pluID
AND b.listPrice = a.listPrice
AND b.unlimitedQty = a.unlimitedQty
AND (b.packageID <=> a.packageID)
AND b.orderID IS NULL
AND c.isOpen = '1'
AND b.paymentTypeID <= '2'
AND (b.inCart < '$cartTime' OR b.inCart IS NULL) ) AS invCount,
(SELECT COUNT(x.inventoryID)
FROM storeInventory x
WHERE x.packageID = a.inventoryID) AS packageCount
FROM storeInventory a
INNER JOIN storePLUs f ON f.pluID = a.pluID
INNER JOIN storeCategories r ON r.catID = f.catID
INNER JOIN events d ON d.eventID = a.eventID
WHERE a.storeFrontID = '1'
AND a.orderID IS NULL
AND a.paymentTypeID <= '2'
AND d.isOpen = '1'
GROUP BY a.packageID, a.unlimitedQty, a.listPrice, a.pluID
I am not familiar with the term 'Composite indexes' Is that something different than these?
Screenshot of ForeignKeys on Table a
I think
AND (b.packageID = a.packageID
OR (b.packageID IS NULL
AND a.packageID IS NULL)
)
can be simplified to ( https://dev.mysql.com/doc/refman/8.0/en/comparison-operators.html#operator_equal-to ):
AND ( b.packageID <=> a.packageID )
Use COUNT(*) instead of COUNT(x.inventoryID) unless you check for not-NULL.
The subquery to compute packageCount seems strange; you seem to count inventories but join on packages.
The need to reach into another table to check isOpen is part of the performance problem. If eventID is not the PRIMARY KEYforevents, then add INDEX(eventID, isOpen)`.
Some other indexes that may help:
a: INDEX(storeFrontID, orderID, paymentTypeID)
a: INDEX(packageID, unlimitedQty, listPrice, pluID)
b: INDEX(pluID, listPrice, unlimitedQty, orderID)
f: INDEX(pluID, catID)
r: INDEX(catID, mastCatID)
x: INDEX(packageID, inventoryID)
After OP's Update
There is no way to do (x<y OR x IS NULL) except by switching to a UNION. In your case, it is pretty easy to do the conversion. Replace
( SELECT COUNT(*) ... AND ( b.inCart < '$cartTime'
OR b.inCart IS NULL ) ) AS invCount,
with
( SELECT COUNT(*) ... AND b.inCart < '$cartTime' ) +
( SELECT COUNT(*) ... AND b.inCart IS NULL ) AS invCount,
Revised indexes:
storePLUs:
INDEX(pluID, catID)
storeCategories:
INDEX(catID, mastCatID)
events:
INDEX(isOpen, eventID)
storeInventory:
INDEX(pluID, listPrice, unlimitedQty, orderID, packageID)
INDEX(pluID, listPrice, unlimitedQty, orderID, inCart)
INDEX(packageID, inventoryID)
INDEX(storeFrontID, orderID, paymentTypeID)
Got a problem here I almost have down but missing a small piece I think. I am taking user info such as width,height,material(s),type, and min. bid/max. bid..I want to use those parameters against a large database I constructed to show only the results that land between the user parameters. As of now I have a query such as:
"SELECT
bp.*, b.company
FROM `DBPREFIXwindows_brands_products` bp
LEFT JOIN `DBPREFIXwindows_brands` b ON bp.brand_id = b.id
WHERE
bp.width = '24'
AND bp.height = '30'
AND bp.material = 'Vinyl' # This is if you use 1 for vinyl
AND bp.type = 'Casement' OR 'Vinyl'
AND (bp.cost >= '500' AND bp.cost <= '1000' )"
This is great if I want to query only static results; however, I have a table called Windows_last_submission that holds information such as width/height/material(s)/etc.. to use againsts the database DBPREFIXwindows_brands / DBPREFIXwindows_brands_products.
Basically how will I substitute, for example, the '24' in 'width' with the information in Windows_last_submissions.width.
Thank you! Hopefully I am clear.
How about:
SELECT bp.*, b.company
FROM `DBPREFIXwindows_brands_products` bp
LEFT JOIN `DBPREFIXwindows_brands` b ON bp.brand_id = b.id
JOIN Windows_last_submissions ls
WHERE bp.width = ls.width
AND bp.height = ls.height
AND bp.material = ls.material
AND bp.type = ls.type
AND (bp.cost BETWEEN ls.minbid AND ls.maxbid )
I want to sort after a value in another table the current table is referenced to. My query looks like this:
SELECT o._id,
o.titel,
o.beschreibung
FROM `objekt` AS o,
`objekt_einzel` AS oe,
`objekt_einzel_immobilie` AS oei,
`objekt_art` AS oa,
`verortung` AS v
#here
,`person` AS p,
`person_bauträger` AS pb
#end
WHERE o._id = oe.objekt_id
AND oe._id = oei.objekt_einzel_id
AND oa._id = o.objekt_art_id
AND o.ort_id = v._id
#here
AND oe.bauträger_id = pb._id
AND pb.person_id = p._id
#end
AND ( oei.justimmo_objekt_id = "0"
OR oei.justimmo_objekt_id IS NULL
OR oei.justimmo_objekt_id = "" )
#here
ORDER BY p.firmenbezeichnung ASC
The query is working fine but it shows me only values if oe.bauträger_id is set. I also want the null values. So I need a left join. I tried different things but I only get messages like unknown column or I get too much results.
I tried to simplify it to this:
SELECT o._id,
o.titel,
o.beschreibung
FROM `objekt` AS o,
`objekt_einzel` AS oe,
(SELECT oe.bauträger_id
FROM objekt o, objekt_einzel oe, objekt_einzel_immobilie oei
WHERE o._id = oe.objekt_id AND oe._id = oei.objekt_einzel_id) AS menge1
LEFT JOIN
(SELECT pb._id AS bauträger_id
FROM person p, person_bauträger pb
WHERE p._id = pb.person_id) AS menge2
ON menge1.bauträger_id = menge2.bauträger_id
WHERE o._id = oe.objekt_id AND oe.bauträger_id = menge1.bauträger_id
but here I get a too big result set. I don't know how to explain this better. The data sets are too big to create an example. I hope you understand what I mean.
SELECT o._id,
o.titel,
o.beschreibung
FROM `objekt` AS o
JOIN `objekt_einzel` AS oe ON o._id = oe.objekt_id
JOIN `objekt_einzel_immobilie` AS oei ON oe._id = oei.objekt_einzel_id
JOIN `objekt_art` AS oa ON o.objekt_art_id = oa._id
JOIN `verortung` AS v ON o.ort_id = v._id
LEFT JOIN `person_bauträger` AS pb ON oe.bauträger_id = pb._id
LEFT JOIN `person` AS p ON pb.person_id = p._id
WHERE oei.justimmo_objekt_id = "0"
OR oei.justimmo_objekt_id IS NULL
OR oei.justimmo_objekt_id = ""
ORDER BY p.firmenbezeichnung ASC
This second try should work as it is just the original code rewritten using JOIN syntax and with LEFT JOINs.
I have the following code:
$params = array();
$query_questions_visibles = questionTable::getInstance()->createQuery("q")
->select("q.*, ua.*, a.*, u.*, c.*")
->leftJoin("q.Answers a")
->leftJoin("a.UserAnswers ua")
->Where("q.blocked = false")
->andWhere("ua.user_id = :user_id")
->orderBy("q.id DESC")
->groupBy("q.id");
//Subquery --> Calculates when a question is active or not
$format = sfConfig::get("app_datetime_format");
$active_time = date($format, strtotime(sfConfig::get("app_question_active_time")));
$sub_query_is_active = $query_questions_visibles->createSubQuery()
->select("MAX(ua0.created_at)")
->from("question q0")
->leftJoin("q0.Answers a0")
->leftJoin("a0.UserAnswers ua0")
->where("q0.id = q.id");
$query_questions_visibles->addSelect("COALESCE((".$sub_query_is_active.") > :active_time, false) as Active");
//Set param values
$params["user_id"] = $guardUser->id;
$params["active_time"] = $active_time;
$result = $query_questions_visibles->execute($params);
The previous code works as expected.
Generated SQL is complete and works:
SELECT q.id AS q__id, q.user_id AS q__user_id, q.category_id AS q__category_id, q.gender_restriction AS q__gender_restriction, q.question AS q_question, q.photo AS q_photo, q.latitude AS q_latitude, q.longitude AS q_longitude, q.multiple AS q_multiple, q.blocked AS q_blocked, q.created_at AS q__created_at, q.updated_at AS q__updated_at, a.id AS a__id, a.question_id AS a__question_id, a.text AS a_text, u.id AS u_id, u.user_id AS u__user_id, u.answer_id AS u__answer_id, u.created_at AS u__created_at, u.updated_at AS u__updated_at, COALESCE((SELECT MAX(u2.created_at) AS u2__0 FROM question q2 LEFT JOIN answer a2 ON q2.id = a2.question_id LEFT JOIN user_answer u2 ON a2.id = u2.answer_id WHERE (q2.id = q.id)) > :active_time, 0) AS u2__0 FROM question q LEFT JOIN answer a ON q.id = a.question_id LEFT JOIN user_answer u ON a.id = u.answer_id WHERE (q.blocked = 0 AND u.user_id = :user_id) GROUP BY q.id ORDER BY q.id DESC
But, if I want to limit results, I modify the end lines as:
$query_questions_visibles->limit(10);
$result = $query_questions_visibles->execute($params);
Doctrine throws an error:
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens
at Doctrine_Connection->execute('SELECT DISTINCT q2.id FROM question q2 LEFT JOIN answer a2 ON q2.id = a2.question_id LEFT JOIN user_answer u2 ON a2.id = u2.answer_id WHERE q2.blocked = 0 AND u2.user_id = :user_id GROUP BY q2.id ORDER BY q2.id DESC LIMIT 10', array('user_id' => '1', 'active_time' => '2013-03-17 17:12:12')) in SF_ROOT_DIR\lib\vendor\symfony\lib\plugins\sfDoctrinePlugin\lib\vendor\doctrine\Doctrine\Query.php line 1290 ...
My purpose is only limit the query to 10 results, ¿where is my Subquery with COALESCE and MAX functions? ¿Why there's a SELECT DISTINCT who I never specified? ¿Why is selecting only q.id?
I spend all day trying to figure it out, i have no answer...
Any Ideas why setting a limit causes this?
When you add limit() to the Doctrine query then the Doctrine internals create in fact two queries. The first one select a limited set of distinct ids based on the conditions of your query. The second query selects the actual objects limiting the select to the ids found with the first query.
The problem with your query is that you use params inside of the select part, which is not used in the first query.
The only solutuion that comes to my mind is to add the value of the active_time parameter directly to the select part, without using the named param. It might not be the nicest solution but I can't think of a different one right now. The addSelect() does not accept additional parameters like where() does which could fix the issue (with where() you can use: ->where('field > ?', $value)).
This is the query I'm talking about:
SELECT COUNT
FROM Note N, DatePCC D
WHERE D.codClient = '2' AND
((D.CodPCC = N.CodPCC AND D.TipPCC = N.TipPCC) OR (D.CodPCC = N.CodPCCOA AND D.TipPCC = N.Relatie))
AND (N.user = 'Server' OR N.user = 'ADMIN' OR N.user = 'Simona' OR N.user =
'Viorel' OR N.user = 'Dan' OR N.user = 'Razvan') AND (N.TipNota = 'Telefon' OR N.TipNota =
'Messenger' OR N.TipNota = 'Telefon esuat' OR N.TipNota = 'Email' OR N.TipNota = 'Evaluare
curs' OR N.TipNota = 'Corespondenta' OR N.TipNota = 'Vizita') AND
(DATE(N.DataInregistrarii) >= '2000-01-01' AND DATE(N.DataInregistrarii) <= '2012-01-25')
AND 1
The problem come from the line i've singled out. When I use it as simply
(D.CodPCC = N.CodPCC AND D.TipPCC = N.TipPCC)
the query runs almost instantly but after I add the second part it locks up our database. I can't, for the life of me, figure out why it impacts the duration so much since it's simply another two comparisons among many other.
What the query does is select stuff from N X D Where either these two (CodPCC, TipPCC) from N coincide with CodPCC and TipPCC from D or (CodPCCOA, Relatie) from N coincide with the same two rows from D.
Any idea how those two simple comparisons are changing the query in such a huge way? or how I could change it to work properly?
The big difference is because you are changing a simple inner join into an expression that has to be evaluated on a cross join between the tables.
You shouldn't write your join that way in the first place, but use the join keyword:
select COUNT
from Note N
inner join DatePCC D on D.codClient = '2' and D.CodPCC = N.CodPCC and D.TipPCC = N.TipPC
where ...
To make the new query efficient, you should make two left joins against the same table. That way the database can use index for each join instead of making a cross join.
select COUNT
from Note N
left join DatePCC D on D.codClient = '2' and D.CodPCC = N.CodPCC and D.TipPCC = N.TipPC
left join DatePCC D2 on D2.codClinent = '2' and D.CodPCC = N.CodPCCOA AND D.TipPCC = N.Relatie
where ...
You can also try if an inner join is efficient with the two sets of conditions, but it's possible that the database can't optimise that into a good query.