set a mysql user variable per row? - mysql

The following SQL statement sets a user variable #id which I'm then using in the trailing subquery. My problem is the #id variable only gets set to the first items.id in the result set - not "per row" - so the result from the subquery is the same for every row in the main select. Does anyone know if there's a way to reset the #id variable for each row??
select
items.id
,items.title
,#id := items.id
,(
select
group_concat(x.y)
from
(
select
group_concat(ledger.stockcode) as y
from
ledger
where
#id = ledger.itemid
group by
ledger.stockcode
having
sum(ledger.stockqty) > 0
) as x
) as extras
from
items
where
items.id = 196

Try this query
select
a.id
,a.title
,b.y as extras
from
items a
inner join
(select
group_concat(ledger.stockcode) as y
from
ledger
where
ledger.itemid = 196
group by
ledger.stockcode
having
sum(ledger.stockqty) > 0) b
on
a.id = 196 AND a.id = b.itemid

Related

SQL - Divide One Query by Another

I am trying to create a query which returns the workout percentage completion...
Workout Percentage Completion =
((Sum of LogEntries WHERE date = IN list of dates and WorkoutID =1 ) /
(Sum of all set numbers WHERE WorkoutID = 1))
x 100
Below is what I currently have, at the moment it is only returning the result of the first query.
What should I change for the query to run correctly?
SELECT
(
(
SELECT COUNT(LogEntriesID)
FROM LogEntriesTable
LEFT JOIN ExerciseWorkoutJunctionTable
ON ExerciseWorkoutJunctionTable.ExerciseWorkoutJunctionID =
LogEntriesTable.JunctionID
WHERE LogEntriesTable.date IN (
"14-05-2020", "15-05-2020", "16-05-2020", "17-05-2020",
"18-05-2020", "19-05-2020", "20-05-2020"
)
AND ExerciseWorkoutJunctionTable.WorkoutID = 1
) / (
SELECT sum(SetNumber)
FROM ExerciseWorkoutGoalsTable
LEFT JOIN ExerciseWorkoutJunctionTable
ON ExerciseWorkoutJunctionTable.ExerciseWorkoutJunctionID =
ExerciseWorkoutGoalsTable.JunctionID
WHERE ExerciseWorkoutJunctionTable.WorkoutID = 1
)
)
Your first SELECT statement is doing an OUTER JOIN but then you have a WHERE clause that is selecting non-NULL values from the ExerciseWorkoutJunctionTable table, so I suspect you might as well be doing an INNER JOIN.
When you have two queries, try:
SET #sum = (SELECT SUM(SetNumber) etc ....);
SELECT (COUNT(LogEntriesID) * 100 / #sum) AS percentage
FROM etc.
If you are using MySQL >= 8.0 you should be able to use window functions like this which breakdown your query into more readable sections.
with entries as (
SELECT COUNT(LogEntriesID) as log_entry_count
FROM LogEntriesTable as l
LEFT JOIN ExerciseWorkoutJunctionTable as e ON
e.ExerciseWorkoutJunctionID = l.JunctionID
WHERE l.date IN ("14-05-2020","15-05-2020","16-05-2020","17-05-2020","18-05-2020","19-05-2020","20-05-2020")
AND e.WorkoutID = 1
),
sets as (
SELECT sum(SetNumber) as set_sum
FROM ExerciseWorkoutGoalsTable as eg
LEFT JOIN ExerciseWorkoutJunctionTable ej
ON ej.ExerciseWorkoutJunctionID = eg.JunctionID
WHERE ej.WorkoutID = 1
)
select ((select log_entry_count from entries) / (select set_sum from sets)) * 100 as workout_completion_pct

mysql query group by totals

I am using the following query to retrieve the number of events per state from 2 tables that are linked by a userID.
SELECT state,COUNT(*) AS num
FROM tableUserInfo
WHERE userID IN (SELECT userID
FROM tableEvents
WHERE conditionOne = 1
AND conditionTwo = 2)
GROUP BY state
This query works correctly. My problem is that not all states have user entries, and I need the query to return 0 for those. I was wondering if there was a method such as joining or using an in clause, that would included a set of all states, making the query return 0 for any that didn't have entries in tableEvents?
Do you have a list of states? If not then this would give a list of all the states your database knows about:
SELECT DISTINCT state FROM tableUserInfo
....and enclosing this in brackets it can be dropped in place in the query below:
SELECT s.state, IFNULL(cnt, 0) AS num
FROM list_of_states s
LEFT JOIN (
SELECT state,COUNT(*) AS cnt
FROM tableUserInfo ui
INNER JOIN tableEvents te
ON ui.userId=te.userId
WHERE conditionOne = 1
AND conditionTwo = 2
GROUP BY state
) u
ON s.state=u.state;
Although in the absence of "list_of_states" it would be more efficient to do this:
SELECT ui.state, SUM(IF(te.userId IS NULL, 0, 1)) AS cnt
FROM tableUserInfo ui
LEFT JOIN tableEvents te
ON ui.userId=te.userId
AND te.conditionOne = 1
AND te.conditionTwo = 2
GROUP BY state;
As #raymond-nijland suggested you can use Left Join to include all states.
SELECT tableUserInfo.state,COUNT(tableUserInfo.*) AS num
FROM tableUserInfo Left Join tableEvents on tableUserInfo.userID = tableEvents.userID
WHERE tableEvents.conditionOne = 1 AND tableEvents.conditionTwo = 2
GROUP BY state

Inner join returning more results even after excluding parameters

My actuall query is:
SELECT rrr.extern_OD_id,
rrr.refund_it_amount,
rrr.refund_amount,
rrr.invoice_ref_7,
rrr.invoice_total_amount
FROM
(SELECT return.extern_OD_id,
return.refund_it_amt AS refund_it_Amount,
return.refund_amount,
billing_tbl.invoice_ref_7,
billing_tbl.invoice_total_amount
FROM
(SELECT rrrf.extern_OD_id,
sum(rrrf.refund_it_amt) AS refund_it_amt,
sum(rrrf.refund_amount/100) refund_amount
FROM rrr__refunds_fact rrrf
WHERE rrrf.refund_status = 'completed'
AND rrrf.refund_created_date_key >= '20150401'
AND rrrf.refund_mode IN('CREDIT_CARD',
'CREDIT_EMI',
'DDCHEQUE',
'DEBIT_CARD',
'GIFT_VOUCHER',
'ICICI',
'NETBANKING',
'back_to_source')
AND rrrf.refund_mode NOT IN ('GIFT_Card')
GROUP BY rrrf.extern_OD_id) RETURN
LEFT JOIN
(SELECT invoice_ref_7,
sum(invoice_total_amount) invoice_total_amount
FROM invoice_fact
WHERE invoice_type_key = 'receiv_note'
AND invoice_status_key NOT IN('voided',
'canceled',
'cancelled')
GROUP BY invoice_ref_7) billing_tbl ON RETURN.extern_OD_id =billing_tbl.invoice_ref_7
WHERE RETURN.refund_amount <> billing_tbl.invoice_total_amount
OR billing_tbl.invoice_ref_7 IS NULL) rrr
WHERE rrr.refund_it_amount >0
AND rrr.refund_amount >0
But when I try to join another table and exclude some parameters, it gives me more records than records from table srrr__refunds_fact.
It gives many records from the new joined table i.e. Payment_TBL which are not present in rrr__refunds_fact.
Can you please tell me where I am going wrong.
As per my understanding if the first query is returning n records, after joining the new records should be n or < n
SELECT rrr.extern_OD_id,
rrr.refund_it_amount,
rrr.refund_amount,
rrr.invoice_ref_7,
rrr.invoice_total_amount
FROM
(SELECT return.extern_OD_id,
return.refund_it_amt AS refund_it_Amount,
return.refund_amount,
billing_tbl.invoice_ref_7,
billing_tbl.invoice_total_amount
FROM
(SELECT rrrf.extern_OD_id,
sum(rrrf.refund_it_amt) AS refund_it_amt,
sum(rrrf.refund_amount/100) refund_amount
FROM rrr__refunds_fact rrrf
JOIN Payment_TBL paymt ON paymt.payment_ref_num_2 = rrrf.extern_OD_id
WHERE rrrf.refund_status = 'completed'
AND rrrf.refund_created_date_key >= '20150401'
AND rrrf.refund_mode IN('CREDIT_CARD',
'CREDIT_EMI',
'DDCHEQUE',
'DEBIT_CARD',
'GIFT_VOUCHER',
'ICICI',
'NETBANKING',
'back_to_source')
AND rrrf.refund_mode NOT IN ('GIFT_Card')
AND paymt.payment_method_key NOT IN ('WALLET')
AND paymt.payment_ref_num_4 NOT IN ('PRICE_REFUND')
GROUP BY rrrf.extern_OD_id) RETURN
LEFT JOIN
(SELECT invoice_ref_7,
sum(invoice_total_amount) invoice_total_amount
FROM invoice_fact
WHERE invoice_type_key = 'receiv_note'
AND invoice_status_key NOT IN('voided',
'canceled',
'cancelled')
GROUP BY invoice_ref_7) billing_tbl ON RETURN.extern_OD_id =billing_tbl.invoice_ref_7
WHERE RETURN.refund_amount <> billing_tbl.invoice_total_amount
OR billing_tbl.invoice_ref_7 IS NULL) rrr
WHERE rrr.refund_it_amount >0
AND rrr.refund_amount >0

updating in a query with joins. Counter to be increased by number of rows

I have written a following query.
UPDATE
tbl_bookings tb
INNER JOIN
tbl_slots ts
ON ( tb.slot_id = ts.id )
SET tb.seat_freed = 1, ts.free_machines = ts.free_machines + 1
WHERE 1
AND tb.seat_freed = 0
AND tb.transactionComplete = 0
Here I am trying to free the seats by updating the seat_freed to 1 and increasing the free_machines counter by 1.
In case, there are more than 1 rows (say 3 rows) returned from tbl_bookings, I would want to increment the counter by .
Is there any way to do it, using the single. I can obviously do it by breaking it down into different queries, but single query is what I desire. :)
You could use a subquery with the exact same conditions to calculat the number of rows which will be affected by the update. I used DISTINCT for the count since i don't know how bookings and slots are related in your example.
UPDATE tbl_bookings tb
INNER JOIN tbl_slots ts ON ( tb.slot_id = ts.id )
INNER JOIN (SELECT count(DISTINCT b.id) seats_to_be_freed
FROM tbl_bookings b INNER JOIN tbl_slots s ON ( b.slot_id = s.id )
WHERE b.seat_freed=0 and b.transactionComplete=0) tmp
SET tb.seat_freed = 1, ts.free_machines = tmp.seats_to_be_freed
WHERE 1
AND tb.seat_freed = 0
AND tb.transactionComplete = 0

Is there a way to optimize this update query?

I have a master table called "parent" and a related table called "childs"
Now I run a query against the master table to update some values with the sum from the child table like this.
UPDATE master m SET
quantity1 = (SELECT SUM(quantity1) FROM childs c WHERE c.master_id = m.id),
quantity2 = (SELECT SUM(quantity2) FROM childs c WHERE c.master_id = m.id),
count = (SELECT COUNT(*) FROM childs c WHERE c.master_id = m.id)
WHERE master_id = 666;
Which works as expected but is not a good style because I basically make multiple SELECT querys on the same result. Is there a way to optimize that? (Making a query first and storing the values is not an option.
I tried this:
UPDATE master m SET (quantity1, quantity2, count) = (
SELECT SUM(quantity1), SUM(quantity2), COUNT(*)
FROM childs c WHERE c.master_id = m.id
) WHERE master_id = 666;
but that doesn't work.
Update: Here is the solution, thanks to everbody:
You can do something like this:
UPDATE master m
INNER JOIN childs c ON m.master_id = c.master_id
SET master.quantity1 = c.quantity1,
master.count = 1
If you have only one child record at a time. However if you want to use a group function like SUM() in the joined table that doesn't work. Either you get a "Invalid use of group function" if you leave the "group by" part or a "You have an error in your sql syntax if you use "GROUP BY c.master_id"
-- This doesnt work :(
UPDATE master m
INNER JOIN childs c ON m.master_id = c.master_id
SET master.quantity1 = SUM(c.quantity1),
master.count = COUNT(c.*)
GROUP by c.master_id
The solution is to use JOIN with a subquery:
UPDATE master m
INNER JOIN
(
SELECT master_id,
SUM(quantity1) as quantity1,
COUNT(*) as count
FROM childs c
GROUP BY master_id
) c
ON c.master_id = m.master_id
SET m.quantity1 = c.quantity1,
m.count = c.count
WHERE m.master_id = 666;
But since this pulls every row from the childtable the overhead would likely be bigger than using more subqueries like in the original sql. So you should add a WHERE clause to the joined table to get only the rows you need.
Another interesting approach is this syntax, which does the same as the JOIN with the WHERE clause but you should only use if if you want to update all rows with the same values and your subquery only returns one row, since the result from the subquery gets appended to the result and can be used like any column.
UPDATE master m,
(
SELECT SUM(c.quantity1) as sum_of_quantity,
COUNT(*) as rowcount FROM child c WHERE c.master_id = 666
) as c
SET m.quantity1 = c.sum_of_quantity,
m.count = c.rowcount
WHERE m.master_id = 666;
Rewriting Lieven's solution to MySQL:
UPDATE master m
JOIN (
SELECT master_id
, SUM(quantity1) as quantity1
, SUM(quantity2) as quantity2
, COUNT(*) as count
FROM childs c
GROUP BY
master_id
) c
ON c.master_id = m.master_id
SET
m.quantity1 = c.quantity1
,m.quantity2 = c.quantity2
,m.count = c.count
WHERE m.master_id = 666;
I don't know if it is allowed in MySQL, but SQL Server allows you to use the result of a select in an update.
UPDATE master m SET
quantity1 = c.quantity1
, quantity2 = c.quantity2
, count = c.count
FROM master m
INNER JOIN (
SELECT master_id
, quantity1 = SUM(quantity1)
, quantity2 = SUM(quantity2)
, count = COUNT(*)
FROM childs c
WHERE master_id = 666
GROUP BY
master_id
) c ON c.master_id = m.master_id
You could select your data into a temporary table, and then update using that data.
If you also want to insert "new" data in the same roundtrip, look into INSERT INTO ... SELECT FROM ... ON DUPLICATE KEY UPDATE ...
If you already are doing inserts if row doesn't exist, then that would be redundant with this example.
example:
INSERT INTO master m (id, quantity1, quantity2, count)
SELECT master_id, SUM(quantity1) q1, SUM(quantity2) q1, COUNT(*) c
FROM childs
GROUP BY master_id
ON DUPLICATE KEY UPDATE
m.quantity1 = q1,
m.quantity2 = q2,
m.count = c
NOTE! This is untested code, but I think it should be possible to backreference the select result in the UPDATE.
Syntax reference: http://dev.mysql.com/doc/refman/5.0/en/insert.html