MySQL Function only once with group by - mysql

I have the following statement:
SELECT user_id, myMysqlFunction(user_id) FROM users_to_days GROUP BY user_id;
The table stores one row per user and day.
The problem is that MySql calls the Function "myMysqlFunction(user_id)" for every row and groups the rows after that.
My target is to execute the Function just once (for performance reasons).
I tried the following state without success:
SELECT user_id, IF(`date` = min(`date`),myMysqlFunction(user_id),0) FROM users_to_days GROUP BY user_id;
MySql still executes the statement for every row.
If I write a static date (for example '2014-09-01') - it works:
SELECT user_id, IF(`date` = '2014-09-01',myMysqlFunction(user_id),0) FROM users_to_days GROUP BY user_id;
But I don't know the min. date for the user at the time of execution. (its not the same for every user)

you could try a subquery:
SELECT user_id, myMysqlFunction(user_id)
FROM (SELECT user_id
FROM users_to_days
GROUP BY user_id);

Edit: comment got split up, I wanted to say:
I meant: try $date = date('Y-m-d');, that will get the time the user runs it. Put that in min($date), and it should be working.

Related

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

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

Get Conditionally Latest record from each group - without Aggregate functions or Partition

I have been trying to do this in many ways suggested.
Note: we do not want aggregate function or Partition since this is just a small part of whole Stored procedure and this is client requirement to not have it, so not in option and not possible duplicate of other existing answers / questions
I have a messages table, which has a column from and to, a foreign key to the user table, basically which user sends to whom at simplest. I also have other columns which are isSnoozed and snoozeAt for if the message is snoozed.
So the ordering is according to case. If messages is snoozed then consider snoozeAt time to Order or if not then consider sendAt. (right now we can ignore this condition while ordering, But I mentioned this since we cannot take simply MAX(id) )
I need to get recent most message from messages group by from user id
messages table like :
id -- to -- from -- isSnoozed -- snoozedAt -- sendAt ...
What I tried :
select * from ( select * from messages order by sendAt DESC) as TEMP GROUP BY TEMP.from
I tried many similar approaches but none worked.
I wasted many paid hours but can't find an approach which meets my exact requirement
NOTE: Please ignore typo in query if any, since I cant type in exact query table and names, So i typed in directly here
I figured this out by doing something like this, which could be explained in a simplified way:
select * from message where message.id in (
select
( select id from message where message.from = user.id order by CASE isSnoozed WHEN 0 THEN sendAt ELSE snoozeAt END DESC limit 1) as id
from user where user.id in ( select friends.`whoIsAdded` from friends where friends.`whoAdded` = myId)
) order by CASE isSnoozed WHEN 0 THEN sendAt ELSE snoozeAt END DESC
If I understand correctly, you just want the largest value in one of two columns. Assuming the values are never NULL, you can use greatest():
select m.*
from messages m
where greatest(m.sendAt, m.snoozedAt) =
(select max(greatest(m2.sendAt, m2.snoozedAt))
from messages m2
where m2.from = m.from
);
If the columns can be NULL, then you can use coalesce() to give them more reasonable values.

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)

Why does MySQL query not return newest row

I got specific query:
SELECT *
FROM stats
WHERE mod_name = 'module'
GROUP BY domain
ORDER BY addition_date DESC
I want to retrive, the newest value for every domain. I know there is a domain x.com value with date 2014-02-19.
However, this query returns me row with date: 2014-01-06
That's quite simple query... why it does not take group by domains and take only newest value?
Am I missing something?
The order by takes place after the group by. That is why your query does not work. Here is a way to get what you want:
SELECT s.*
FROM stats s
WHERE mod_name = 'module' and
not exists (select 1
from stats s2
where s2.mod_name = s.mod_name and
s2.addition_date > s.addition_date
)
ORDER BY addition_date DESC;
To get the best performance, create an index on stats(mod_name, addition_date).

MySQL Query: Query with conditional statements

i have this query
SELECT
IF(isnull(ub.user_lecture_id), 0, ub.user_lecture_id) as IsPurchased,
cs.title,cs.start_date, cs.start_time, cs.end_time
FROM campus_schedule cs
LEFT JOIN campus_bookinfo cb ON cs.bookid=cb.idx_campus_bookinfo
LEFT JOIN user_lectures ub ON ub.id_product = cs.idx_campus_schedule AND ub.id_customer = 11
WHERE cs.idx_campus = 1 and cs.title like '%%' and cs.status=1
Which Shows:
Click to view Output
Explanation: if (IsPurchased == 0) it is not yet bought my customer
My Question: if you look at the time of row with IsPurchased=1, the time range is conflicting with the time in IsPurchases=0. how can i compare and conclude that the time of the same date of the query is conflicting to the time and date of the other rows. results may be 1 or 0 in a "conflict" field name
Hope you got the point. Thanks for the help!!!
To compare times, you will find it easier to use DATETIME fields.
To check for "conflicting" rows, you'll probably need to have a subquery in the WHERE clause.
Subquery should work but will be inefficient in mysql. You should create temporary table and analyze it. Or do the same inline, like:
set #lastdate=0;
set #lasttime=0;
select IsPurchased, title, start_date, start_time, end_time, if(#lastdate = start_date, #lasttime < end_time, 1) as CONFLICT, #lastdate:=start_date, #lasttime:=start_time
from (your_query ORDER BY start_date, start_time, end_time) t ;
that is just an idea, it worked for me several times.