Unknown Column in Where Clause When Using the Variable Created with AS - mysql

The question is about using a column (which is created in query) as where clause criteria.
There are 2 tables named transactions and transactionmovements.
In transaction, there are unique info for transaction like date, counterparty etc.
In transactionmovements, there are articles which is used in transaction. Such as product, quantity, price, etc. And transactionmovements has a 'transaction' column which references to transactions.id shows which transaction the movement belongs.
In the query, I created a totalPrice value with sum of quantity*price of each movement that belongs to a transaction.
Everything works perfectly but the last parameter of WHERE clause. If I delete "AND
totalPrice > 10" part, it gives me everything including totalPrice and totalQuantity of a transaction.
But if I place "AND totalPrice > 10" to the end, it returns following error:
-#1054 - Unknown column 'totalPrice' in 'where clause'
SELECT
`transactions`.id,
`transactions`.type,
`transactions`.date,
`transactions`.VAT,
`transactions`.currency,
`companies`.name AS counterparty,
COALESCE
(sum(`transactionmovements`.price*`transactionmovements`.quantity)
+(`transactions`.shippingQuantity*`transactions`.shippingPrice)) as totalPrice,
COALESCE
(sum(`transactionmovements`.quantity)) as totalQuantity
FROM
`transactions`
LEFT JOIN `companies` ON `transactions`.counterparty = `companies`.id
LEFT JOIN `transactionmovements` ON `transactions`.id=`transactionmovements`.transaction
WHERE ( `transactions`.type = 'p' OR `transactions`.type = 'r' OR `transactions`.type = 's' OR `transactions`.type = 't')
AND
(`transactions`.date BETWEEN IFNULL('','1900-01-01') AND IFNULL('2020-02-14',NOW()))
AND
totalPrice > 10
GROUP BY `transactions`.id
ORDER BY id desc
LIMIT 10
I tried using the whole math operation in WHERE clause, but no gains. I tried to use HAVING with WHERE but couldn't manage it.
The last solution I have is running it without filtering by totalPrice and store it into a php array. Then filter in array, but there I can't use LIMIT so array will be very big.

As per how SQL query are executed in the order that dictate SELECT is executed after WHERE, you can't use ALIAS in WHERE since it is unknown at the moment. Therefor, you should change every alias uses outside of SELECT to its definition.

As Thomas Jeriko suggested in comments, creating a view was exactly what I need.
First I create a virtual table with Create View.
CREATE VIEW transactionreportview
AS SELECT
transactions.id id,
transactions.type type,
transactions.date date,
transactions.VAT VAT,
transactions.currency currency,
transactions.counterparty counterparty,
(SUM(transactionmovements.price*transactionmovements.quantity)+transactions.shippingQuantity*transactions.shippingPrice) totalPrice,
SUM(transactionmovements.quantity) totalQuantity
FROM transactions transactions, transactionmovements transactionmovements
WHERE transactions.id = transactionmovements.transaction
GROUP BY transactions.id;
While the virtual table acts like a simple mysql table, I ran a new query in the table
SELECT * FROM transactionReportView WHERE ..."
Then after finishing my work with the virtual table, drop it
DROP VIEW transactionreportview

Related

MySQL Correlated sub query table name out of scope

This form of my correlated sub query comes up with the error message "Unknown column 'Invoices.TranDate' in 'where clause'"
select InvoiceID, TranDate
, ifnull(TotPayments,0) TotPayments, ifnull(CountPayments,0) CountPayments
from Invoices
left join (select DebtorID, sum(TranAmount) TotPayments, count(*) CountPayments
from CashTrans
where CashTrans.TranDate >= Invoices.TranDate
group by DebtorID) PY on PY.DebtorID = Invoices.DebtorID
Yet this version works
select InvoiceID, TranDate
, (select sum(TranAmount) from CashTrans
where CashTrans.TranDate >= Invoices.TranDate
and CashTrans.DebtorID = Invoices.DebtorID) TotPayments
, (select count(*) from CashTrans
where CashTrans.TranDate >= Invoices.TranDate
and CashTrans.DebtorID = Invoices.DebtorID) CountPayments
from Invoices;
What is wrong with the first query? The only thing I can think of is that on my Windows system I have configured lower_case_table_names=2 as I want to preserve mixed case names. Perhaps that has something to do with the first query not seeing Invoice.TranDate in scope? MySQL Documentation and internet searches have not thrown any light on the matter.
https://dev.mysql.com/doc/refman/8.0/en/lateral-derived-tables.html says:
A derived table cannot normally refer to (depend on) columns of preceding tables in the same FROM clause. As of MySQL 8.0.14, a derived table may be defined as a lateral derived table to specify that such references are permitted.
In SQL:1999, the query becomes legal if the derived tables are preceded by the LATERAL keyword (which means “this derived table depends on previous tables on its left side”):
I have not tested it, but I believe your query could be written this way:
SELECT InvoiceID, TranDate,
IFNULL(TotPayments,0) AS TotPayments,
ifnull(CountPayments,0) AS CountPayments
FROM Invoices
LEFT JOIN LATERAL (
SELECT DebtorID,
SUM(TranAmount) AS TotPayments,
COUNT(*) AS CountPayments
FROM CashTrans
WHERE CashTrans.TranDate >= Invoices.TranDate
GROUP BY DebtorID
) AS PY ON PY.DebtorID = Invoices.DebtorID;
Also be aware this requires you to use at least MySQL 8.0.14.

Need assistance with SQL Query with error message Operand should contain 1 column(s)

Good Day
I have the following query but I'm getting an error message 'Operand should contain 1 column(s)'
Any assistance would be greatly appreciated
UPDATE expenditure
SET BP = (
SELECT * ,
SUM(balance_provision - actual_amt_voucher) over (partition by voteid order by expenditureid) AS BalanceProvision
FROM expenditure
)
It looks like you want to update column bp with a window sum.
Your query fails because you are trying to assign a resultset (that has multiple columns) to a single column.
But even you were returning a scalar value from the subquery, this would not work, since MySQL does not allow reusing the target table of the update in a subquery.
Instead, yo can use the update ... join syntax. Assuming that expenditureid is the primary key of the table, as its name suggests, that would be:
update expenditure e
inner join (
select
expenditureid,
sum(balance_provision - actual_amt_voucher) over (partition by voteid order by expenditureid) bp
from expenditure
group by expenditureid
) e1 on e.expenditureid = e1.expenditureid
set e.bp = e1.bp

MYSQL, Creating a view and pulling information from two tables

Okay so I have two tables:
hscust and hssales_rep
I need to create a view that shows me the reps fname and lname (as well as the customers) and show how much the customer is over on there credit balance.
This is the code I have:
CREATE VIEW OverLimit AS
SELECT
CONCAT(hssales_rep.last,hssales_rep.first) AS Rep,
CONCAT(hscust.last,hscust.first) AS Cust,
SUM(credit_limit - balance)
FROM hscust
INNER JOIN hssales_rep ON hscust.sales_rep = hssales_rep.repid
And it returns an empty result.
Any help is greatly appreciated!
salesrep table
cust table
A CREATE VIEW statement doesn't return a resultset.
A SELECT statement can return an empty resultset. But we'd expect the SELECT statement in your view definition to return either a single row, or throw an error.
I suggest you break this down a bit.
1) What problem is being solved by the CREATE VIEW statement. Why do you need a view?
2) Before you write a CREATE VIEW statement, first develop and test a SELECT statement that returns the required resultset. Do that before you put that into a view definition.
I also strongly recommend that you qualify all column references in the SELECT statement either with the table name or (preferably) a short table alias.
If you want to return a row for each Cust with an aggregate function (e.g. SUM) in your SELECT list, then add an appropriate GROUP BY clause to your SELECT statement.
It's not clear why we would want to use a SUM aggregate function.
The difference between "credit_limit" and "balance" would be the available (remaining) credit. A negative value would indicate the balance was "over" the credit limit.
SELECT CONCAT(r.last,r.first) AS Rep
, CONCAT(c.last,c.first) AS Cust
, c.credit_limit - c.balance AS available_credit
FROM hscust c
JOIN hssales_rep r
ON c.sales_rep=r.repid
ORDER
BY CONCAT(r.last,r.first)
, CONCAT(c.last,c.first)
, c.custid
If we only want to return rows for customers that are "over" their credit limit, we can add a WHERE clause.
SELECT CONCAT(r.last,r.first) AS Rep
, CONCAT(c.last,c.first) AS Cust
, c.credit_limit - c.balance AS available_credit
FROM hscust c
JOIN hssales_rep r
ON c.sales_rep=r.repid
WHERE c.credit_limit - c.balance < 0
ORDER
BY CONCAT(r.last,r.first)
, CONCAT(c.last,c.first)
, c.custid
Again, get a SELECT statement working (returning the required resultset) before you wrap it in a CREATE VIEW.

Getting previous row in MySQL

I'm stucked in a MySQL problem that I was not able to find a solution yet. I have the following query that brings to me the month-year and the number new users of each period in my platform:
select
u.period ,
u.count_new as new_users
from
(select DATE_FORMAT(u.registration_date,'%Y-%m') as period, count(distinct u.id) as count_new from users u group by DATE_FORMAT(u.registration_date,'%Y-%m')) u
order by period desc;
The result is the table:
period,new_users
2016-10,103699
2016-09,149001
2016-08,169841
2016-07,150672
2016-06,148920
2016-05,160206
2016-04,147715
2016-03,173394
2016-02,157743
2016-01,173013
So, I need to calculate for each month-year the difference between the period and the last month-year. I need a result table like this:
period,new_users
2016-10,calculate(103699 - 149001)
2016-09,calculate(149001- 169841)
2016-08,calculate(169841- 150672)
2016-07,So on...
2016-06,...
2016-05,...
2016-04,...
2016-03,...
2016-02,...
2016-01,...
Any ideas: =/
Thankss
You should be able to use a similar approach as I posted in another S/O question. You are on a good track to start. You have your inner query get the counts and have it ordered in the final direction you need. By using inline mysql variables, you can have a holding column of the previous record's value, then use that as computation base for the next result, then set the variable to the new balance to be used for each subsequent cycle.
The JOIN to the SqlVars alias does not have any "ON" condition as the SqlVars would only return a single row anyhow and would not result in any Cartesian product.
select
u.period,
if( #prevCount = -1, 0, u.count_new - #prevCount ) as new_users,
#prevCount := new_users as HoldColumnForNextCycle
from
( select
DATE_FORMAT(u.registration_date,'%Y-%m') as period,
count(distinct u.id) as count_new
from
users u
group by
DATE_FORMAT(u.registration_date,'%Y-%m') ) u
JOIN ( select #prevCount := -1 ) as SqlVars
order by
u.period desc;
You may have to play with it a little as there is no "starting" point in counts, so the first entry in either sorted direction may look strange. I am starting the "#prevCount" variable as -1. So the first record processed gets a new user count of 0 into the "new_users" column. THEN, whatever was the distinct new user count was for the record, I then assign back to the #prevCount as the basis for all subsequent records being processed. yes, it is an extra column in the result set that can be ignored, but is needed. Again, it is just a per-line place-holder and you can see in the result query how it gets its value as each line progresses...
I would create a temp table with two columns and then fill it using a cursor that
does something like this (don't remember the exact syntax - so this is just a pseudo-code):
#val = CURSOR.col2 - (select col2 from OriginalTable t2 where (t2.Period = (CURSOR.Period-1) )))
INSERT tmpTable (Period, NewUsers) Values ( CURSOR.Period, #val)

How to CAST an Alias

I am trying to calculate a due amount summing and grouping more amounts in table i_payment_history comparing then the result with the total "to_be_payed" amount saved in table pt_bookings. All involved fields are decimal(8,2), i have tryied with Cast becouse without is not working eather. The value "Due" is just the result of 'to_be_payed'-'payed'
so the first value in column "Due" should be 755,10-200,00=535,10 .
Why is "Due" calculating a wrong value? Txs !
SELECT i_payments_history.booking_id, Cast(SUM(amount)AS DECIMAL(8,2)) as payed, pt_bookings.booking_total, pt_bookings.booking_ref_no, (pt_bookings.booking_total - payed) as due
FROM `i_payments_history`
JOIN pt_bookings
on pt_bookings.booking_id= i_payments_history.booking_id
GROUP BY booking_id
see in below pic what the query is returning
Repeat the expression:
SELECT ph.booking_id, Cast(SUM(ph.amount) AS DECIMAL(8,2)) as payed, b.booking_total, b.booking_ref_no,
(b.booking_total - SUM(ph.amount)) as due
FROM `i_payments_history` ph JOIN
pt_bookings b
on b.booking_id = ph.booking_id
GROUP BY b.booking_id;
An alias cannot be used in the same select where it is defined. One alternative is to use a subquery; another is to repeat the expression. Your query did not return an error because one of your tables must have a column named payed.