Adding a WHERE condition on an IF alias in a MySQL query - mysql

I'm having trouble adding a condition on aliases is_paid, is_overdue and is_outstanding in the following query:
SELECT r.doc_number,
r.doc_date,
r.due_date,
r.currency,
r.amount,
r.vat,
r.vatammount,
(r.amount + r.vatammount) final_amount,
r.currency,
b.boq_id,
b.boq_comp_id,
b.boq_client_id,
b.boq_agency,
b.boq_date,
b.boq_orders,
b.receivable_id,
c.comp_name,
crm.`cn-name-first`,
crm.`cn-name-last`,
bi.inv_path,
(SELECT SUM(amount_recieved)
FROM receivables_payments
WHERE r_id = b.receivable_id) total_amount_received,
IF (r.amount + r.vatammount =
(SELECT SUM(amount_recieved)
FROM receivables_payments
WHERE r_id = b.receivable_id),
'1',
'0') AS is_paid,
IF (CURRENT_DATE >= r.due_date
AND r.amount + r.vatammount !=
(SELECT SUM(amount_recieved)
FROM receivables_payments
WHERE r_id = b.receivable_id),
'1',
'0') AS is_overdue,
IF (r.due_date < CURRENT_DATE
AND r.amount + r.vatammount !=
(SELECT SUM(amount_recieved)
FROM receivables_payments
WHERE r_id = b.receivable_id),
'1',
'0') AS is_outstanding
FROM receivables r
LEFT JOIN boq b ON b.receivable_id = r.id
LEFT JOIN boq_invoices bi ON bi.inv_boq_id = b.boq_id
LEFT JOIN comp_companies c ON c.comp_id = b.boq_comp_id
LEFT JOIN crm_contacts crm ON crm.contact_id = b.boq_client_id
WHERE r.status = 'active'
AND r.doc_type = 'inv'
AND b.boq_status = 'active'
AND is_paid = '1'
ORDER BY r.doc_date DESC LIMIT 10
Is there any way to modify this query and to make it possible to add a condition on those three aliases?

use alias in where condition .. is not allowed . because .is not possibile
the query code is evaluted based on a specified order .. starting from FROM then
WHERE clause and last the SELECT and the column alias so .. when the where is performed the column alias is not available at the query
You could try with an having condition because having work on the result of the query and not on the raw rows value .. (this could have effect on performance ..because all the query is performed and only the result is filtered by having)

Related

SQL left join exclude non matching records

I'm working on this query (running on MySQL 5.6):
SELECT
veicoli_contratti.targa AS targa,
veicoli_contratti.canone_noleggio AS canone,
anag_convenzionati.nome AS convenzionato,
SUM(noleggio_veicoli.fatt_prezzo_totale_noleggio) AS noleggi_incasso,
noleggio_veicoli.modalita_noleggio,
COUNT(DISTINCT noleggio_veicoli.id) AS noleggi
FROM
veicoli_contratti
LEFT JOIN
noleggio_veicoli ON veicoli_contratti.id = noleggio_veicoli.id_veicolo
INNER JOIN
anag_convenzionati ON veicoli_contratti.id_convenzionato = anag_convenzionati.id
WHERE
(veicoli_contratti.data_cancellazione IS NULL)
AND (veicoli_contratti.targa <> '')
AND (noleggio_veicoli.data_cancellazione IS NULL)
AND (anag_convenzionati.data_cancellazione IS NULL)
AND (YEAR(noleggio_veicoli.rientro_data) = 2020)
AND (MONTH(noleggio_veicoli.rientro_data) = 10)
AND ((noleggio_veicoli.stato_noleggio = 'C')
OR (noleggio_veicoli.stato_noleggio = 'F')
)
AND ((noleggio_veicoli.modalita_noleggio = 'S')
OR (noleggio_veicoli.modalita_noleggio = 'OPO')
OR (noleggio_veicoli.modalita_noleggio = 'M')
)
AND (veicoli_contratti.stato = 'OPERATIVA')
GROUP BY anag_convenzionati.id , veicoli_contratti.id , noleggio_veicoli.modalita_noleggio
ORDER BY convenzionato , noleggi DESC , canone DESC , noleggi_incasso DESC ;
I thought LEFT JOIN clause will produce a record even if there's not a matching record in table noleggio_veicoli but this doesn't happen.
The result include just records where a match is found between veicoli_contratti and noleggio_veicoli.
I tried also adding OR noleggio_veicoli.id IS NULL in WHERE clause but it's not the solution.
How can I fix this?
I created an SQL fiddle to try this here
Your understand is correct. However, the where clause is "undoing" the LEFT JOIN. Why?
You have conditions such as this:
AND ((noleggio_veicoli.stato_noleggio = 'C') OR (noleggio_veicoli.stato_noleggio = 'F'))
Well, NULL fails those conditions so non-matches are filtered out. This condition (along with other conditions) should be included in the ON clause. For this one, it looks like:
AND noleggio_veicoli.stato_noleggio IN ('C', 'F')

I want duplicates to be included with my in () statement

I want to include duplicates into my query. I havent succeeded in changing my "in" statements to "joins".
My expected result is a row count of 115.
My result is a row count of 108.
If i do a "Group by" in my first subquery, i get a row count of 108.
select match_id item_0, item_1, item_2, item_3, item_4, item_5, purchase_log
from player_matches where match_id IN
(select x.match_id from (select matches.match_id, picks_bans.team from matches, picks_bans where picks_bans.hero_id = 1 and picks_bans.match_id = matches.match_id and is_pick = true and start_time > 1483228800 ORDER BY start_time DESC) as x
inner join
(select matches.match_id, picks_bans.team from matches, picks_bans where picks_bans.hero_id
/*this is the statement that needs to be tweaked/changed */
IN (2,12,47,4,99) and picks_bans.match_id = matches.match_id and is_pick = true and start_time > 1483228800 ORDER BY start_time DESC) as y on y.match_id=x.match_id and x.team!=y.team)
and hero_id = 1
You can use the opendota inbrowser data-explorer to get a better understanding of my problem.
My subquery (returns 115)
My final Query (returns 108)
How do i get my final query to return 115 row counts?
My query is also really slow, is this because im using "in ()"?
It is because the IN will just check if the value is present. Use INNER JOIN instead:
select a.match_id, a.item_0, a.item_1, a.item_2, a.item_3, a.item_4, a.item_5, a.purchase_log
from player_matches a
INNER JOIN
(
select x.match_id from (select matches.match_id, picks_bans.team from matches, picks_bans where picks_bans.hero_id = 1 and picks_bans.match_id = matches.match_id and is_pick = true and start_time > 1483228800 ORDER BY start_time DESC) as x inner join (select matches.match_id, picks_bans.team from matches, picks_bans where picks_bans.hero_id in (2,12,47,4,5) and picks_bans.match_id = matches.match_id and is_pick = true and start_time > 1483228800 ORDER BY start_time DESC) as y on y.match_id=x.match_id and x.team!=y.team
) b ON a.match_id = b.match_id
WHERE hero_id = 1
Here's a demo from your original link.

Where clause using alias column with IF and IFNULL

I have an mysql query like below:
SELECT
sppt_ticket.*,
IF(sppt_read_support_ticket.ID_aks_user IS NULL,'N', 'Y') AS `read_status`,
IFNULL(readcomment.total_comment, 0) AS unread_comment
FROM
sppt_ticket
LEFT JOIN
sppt_read_support_ticket ON
sppt_ticket.ID_support_ticket = sppt_read_support_ticket.ID_support_ticket AND
ID_aks_user = 1
LEFT JOIN
(SELECT
sppt_comment.ID_support_ticket, SUM(IF(sppt_read_comment.ID_aks_user IS NULL, 1, 0))
AS
total_comment
FROM
sppt_comment
LEFT JOIN
sppt_read_comment
ON
sppt_comment.ID_comment = sppt_read_comment.ID_comment
AND
sppt_read_comment.ID_aks_user = 1
GROUP BY
sppt_comment.ID_support_ticket) AS readcomment ON readcomment.ID_support_ticket = sppt_ticket.ID_support_ticket
What I want to get in where clause is like this
WHERE read_status = 'Y'
I've tried using subquery, but still I didn't get it..
any help?
Have you tried this:
SELECT * FROM
(
-- your original query as a table
SELECT
sppt_ticket.*,
IF(sppt_read_support_ticket.ID_aks_user IS NULL,'N', 'Y') AS `read_status`,
IFNULL(readcomment.total_comment, 0) AS unread_comment
FROM
sppt_ticket
LEFT JOIN
sppt_read_support_ticket ON
sppt_ticket.ID_support_ticket = sppt_read_support_ticket.ID_support_ticket AND
ID_aks_user = 1
LEFT JOIN
(SELECT
sppt_comment.ID_support_ticket, SUM(IF(sppt_read_comment.ID_aks_user IS NULL, 1, 0))
AS
total_comment
FROM
sppt_comment
LEFT JOIN
sppt_read_comment
ON
sppt_comment.ID_comment = sppt_read_comment.ID_comment
AND
sppt_read_comment.ID_aks_user = 1
GROUP BY
sppt_comment.ID_support_ticket) AS readcomment ON readcomment.ID_support_ticket = sppt_ticket.ID_support_ticket
)
as temptable
where read_status = 'Y' -- this should work
Or you can use HAVING instead of WHERE if you do not want to treat your query as a table:
SELECT
sppt_ticket.*,
IF(sppt_read_support_ticket.ID_aks_user IS NULL,'N', 'Y') AS `read_status`,
IFNULL(readcomment.total_comment, 0) AS unread_comment
FROM
sppt_ticket
LEFT JOIN
sppt_read_support_ticket ON
sppt_ticket.ID_support_ticket = sppt_read_support_ticket.ID_support_ticket AND
ID_aks_user = 1
LEFT JOIN
(SELECT
sppt_comment.ID_support_ticket, SUM(IF(sppt_read_comment.ID_aks_user IS NULL, 1, 0))
AS
total_comment
FROM
sppt_comment
LEFT JOIN
sppt_read_comment
ON
sppt_comment.ID_comment = sppt_read_comment.ID_comment
AND
sppt_read_comment.ID_aks_user = 1
GROUP BY
sppt_comment.ID_support_ticket) AS readcomment ON readcomment.ID_support_ticket = sppt_ticket.ID_support_ticket
HAVING read_status = 'Y' -- use HAVING instead of WHERE
The reason why treating your original query as a table works is because of the way values are evaluated in the query. In your original query, the alias read_status cannot be used with the WHERE clause because the actual value might not yet be known when the WHERE clause is evaluated. As documented in Section B.1.5.4, “Problems with Column Aliases”. Treating it as a table ensures that the value for read_status has already been evaluated.
For the HAVING approach, MySQL created an extension to standard SQL that permits references in the HAVING clause to aliased expressions in the select list.

MYSQL SELECT is slow when crossing multiple tables

I have a mysql query which is to return the only 1 record that need to cross multiple table. However, the mysql query is slow when executing.
Query:
SELECT *,
(SELECT TreeName FROM sys_tree WHERE TreeId = Mktg_Unit_Booking.ProjectLevelId) AS PhaseName,
(CASE WHEN ProductType = 'U' THEN (SELECT UnitNo FROM prop_unit pu WHERE pu.UnitId = mktg_unit_booking.UnitId)
ELSE (SELECT BayNo FROM prop_car_park pcp WHERE pcp.CarParkId = UnitId) END) AS UnitNo,
(SELECT CustomerName FROM mktg_customer mc WHERE mc.CustomerId = mktg_unit_booking.CustomerId) AS CustomerName
FROM Mktg_Unit_Booking
WHERE IsDeleted <> '1' AND IsApproved = '1'
AND UnitId = 1110 AND ProductType = 'U'
ORDER BY UnitNo
I have run EXPLAIN in the query and I got this:
Any other suggestion on how to improve the speed of the query?
Thank you!
you are doing the cross product, instead of that you should use join.
Don't use sub-queries in select statement instead use proper join on Mktg_Unit_Booking in after from statement.
you query should something look like :
select
sys_tree.TreeName AS PhaseName,
case
WHEN Mktg_Unit_Booking.ProductType = 'U' then prop_unit.UnitNo
else prop_car_park.BayNo
end as UnitNo,
mktg_customer.CustomerName AS CustomerName
FROM Mktg_Unit_Booking
left join sys_tree on sys_tree.TreeId = Mktg_Unit_Booking.ProjectLevelId
left join prop_unit on prop_unit.UnitId = Mktg_Unit_Booking.UnitId
left join prop_car_park on prop_car_park.CarParkId = Mktg_Unit_Booking.UnitId
left join mktg_customer on mktg_customer.CustomerId = Mktg_Unit_Booking.CustomerId
WHERE IsDeleted <> '1' AND IsApproved = '1'
AND UnitId = 1110 AND ProductType = 'U'
ORDER BY UnitNo;
I have assumed that each table consists of only 1 matching tuple. If there are more then your logic needs to be modified.

mysql update statement using the from clause on the same table

The following is the MS Sql server Update statement
Update
HC_TranDetails
SET
InsPayments = (SELECT IsNull(SUM(ISNULL(CreditAmount,0)),0) From HC_TranDetails TDS
Where TDS.TransactionType = 2
AND TDS.ClaimNo = TD.ClaimNo
AND TDS.LineItemNo = TD.LineItemNo
AND IsNull(TDS.InsPlanRowID,'') <> ''
AND TDS.ReverseEntry <> 1 ),
Adjustments = (SELECT IsNull(SUM(ISNULL(CreditAmount,0)),0) From HC_TranDetails TDS
Where TDS.TransactionType = 8
AND TDS.ClaimNo = TD.ClaimNo
AND TDS.LineItemNo = TD.LineItemNo
AND IsNull(TDS.InsPlanRowID,'') <> ''
AND TDS.ReverseEntry <> 1 ),
FROM
HC_TranDetails TD
Now i am trying the same kind of statement in mysql as follows
UPDATE claimdetails SET balanceAmount = (SELECT IFNULL(SUM(IFNULL(debitamount,0)) - SUM(IFNULL(creditamount,0)),0)
FROM claimdetail CD WHERE CD.claimID = CDS.claimID)
FROM ClaimDetail CDS
But it is showing as syntax Error near 'From ClaimDetail CDS' at line 4
MySQL is squeamish about updates on the same table. The easy fix is to include an extra level of subquery. The proper fix, though, is to use a join
UPDATE claimdetails join
(select claimid, IFNULL(SUM(IFNULL(debitamount,0)) - SUM(IFNULL(creditamount,0)),0) as val
from ClaimDetails
group by claimid
) agg
on claimdetails.claimid = agg.claimid
SET balanceAmount = agg.val;
You can join the table you want to update with a subquery that calculates the balance for each claimid on the other table.
By using LEFT JOIN, it will update all records on table claimdetails. A value of 0 will be updated to any non existent claimid on the subquery.
UPDATE claimdetails a
LEFT JOIN
(
SELECT claimID,
SUM(IFNULL(debitamount, 0)) - SUM(IFNULL(creditamount,0)) bal
FROM claimdetail
GROUP BY claimID
) b ON a.claimID = b.claimID
SET a.balanceAmount = IFNULL(b.bal, 0)