Relevant table structure is:
id(INT), reference_id(INT), status(VARCHAR(20)), date(TIMESTAMP)
This record is updated once per day.
Problem:
I want to display each record where the status field, which is static most of the time, is different to the one on the preceeding day, thus being able to show the date a status changed and display a history.
How?
Pseudo:
Show * where status for today != status for yesterday
Early logic, but my limited knowledge of mysql is causing me to hit a wall.
SELECT * from table_name WHERE DATE(date) = CURDATE()
UNION
SELECT table_name.status AS yesterday_status WHERE DATE(date) = CURDATE() - 1
I was then going to compare them both in perl, which is not so efficient so I'm hoping somebody can share some enlightenment...
Here is my approach. It is not exactly what you were asking for, but if maybe you will like my solution.
http://sqlfiddle.com/#!9/896b4/7
SELECT
today.*,
yesterday.*
FROM t_status today
LEFT JOIN t_status yesterday
ON DATE(yesterday.date)<DATE(today.date)
AND yesterday.reference_id = today.reference_id
AND yesterday.status != today.status
WHERE DATE(today.date) = CURDATE()
ORDER BY yesterday.date DESC
LIMIT 1
;
UPDATE Or probably I misunderstood your goal, and you need only compare yesterdays status, not all past statuses. If so you can try this INNER JOIN:
http://sqlfiddle.com/#!9/dd61f1/4
SELECT
today.*,
yesterday.*
FROM t_status today
INNER JOIN t_status yesterday
ON DATE_ADD(CURDATE(),INTERVAL -1 DAY) = DATE(yesterday.date)
AND yesterday.reference_id = today.reference_id
AND yesterday.status != today.status
WHERE DATE(today.date) = CURDATE();
You are welcome if any questions.
The below query shows the records along with the previous day's record (I assumed that reference_id is also a join citeria, if not, just remove it)
select t1.*, t2.* from table t1
left join table t2 on t1.reference_id=t2.reference_id and date(t1.date)-1=date(t2.date)
You can even filter it further to display only those pairs, where the previous day's status is different (note that the join changes from left to inner, since if there is no previous day's record, that's not a change is status):
select t1.*, t2.* from table t1
inner join table t2 on t1.reference_id=t2.reference_id and date(t1.date)-1=date(t2.date) and t1.status<>t2.status
I think query should be like
select * from tablename where status <> (select * from tablename where date=Sysdate-1)
Sysdate is use for System date.
Related
I am aware of the slow query log but isnt this just for queries that exceed the maximum execution time? My question is slightly different (i think).
I am running the below query and to me it doesnt seem like it should present a problem. But when i run it, the "Running" time is over 20 seconds and then it sits there "Fetching" for ages! To the point where i have to stop it due to DB performance issues.
Can anyone explain where i am going wrong here. Coding newbie so please be gentle.
SELECT
t1.oid,
(SELECT
DATE_FORMAT(t2.date,
'%d/%m/%y') AS 'AVF Date'
FROM
t2
WHERE
t1.oid = t2.fk_oid
AND t2.type = '-25'
AND YEAR(t2.date) BETWEEN 2005 AND 2014
ORDER BY t2.date ASC
LIMIT 1) AS 'AVF Date',
t2.site
FROM
t1
left join t2 on t1.oid=t2.fk_oid
UPDATE - Ok so what i need is as follows. We have a patient database and these patients have procedures recorded on this database. I am trying to return the patient oid (t1.oid) along with the FIRST procedure that they had (if it was between 2005 and 2014) and where they had it (t2.date and t2.site respectively), but only if the procedure is of a particular type (t2.type = '-25')
So basically. All the patients who had an AVF between 2005 and 2014 along with the "site" of the AVF (which in this case is left arm/right arm etc.)
I hope this makes it a bit clearer.
Thanks in advance.
Mikey
I think you can use only the join without the subquery in the select, have a try:
SELECT t1.oid, DATE_FORMAT(t2.date,'%d/%m/%y'),t2.site
FROM table1 AS t1
LEFT JOIN table2 AS t2
ON t1.oid = t2.fk_oid
WHERE
t2.type = '-25'
AND YEAR(t2.date) BETWEEN 2005 AND 2014
ORDER BY t2.date ASC
LIMIT 1;
Ps: I haven't tested the code.
SELECT a.fk_oid AS oid,
DATE_FORMAT(a.first_date, '%d/%m/%y') AS 'AVF Date',
GROUP_CONCAT(b.site) AS "site(s)"
FROM
( SELECT fk_oid,
MIN(date) AS first_date
FROM site
WHERE date > CURDATE() - INTERVAL 10 YEAR
AND date <= CURDATE()
AND t2.type = -25
GROUP BY fk_oid
) AS a
JOIN site AS b
WHERE b.fk_oid = a.fk_oid
AND b.date = a.first_date ;
The inner query finds the first_date for each oid.
The outer query gets the site, but further assumes there might have been multiple sites for a given oid and date.
INDEX(type, date) is needed for performance
INDEX(fk_oid, date) is needed for performance
I assumed type is a number, not a string.
I rewrote the date comparison to imply "in the last 10 years". If there are no rows for the future, get id of AND date <= CURDATE().
Since you seem to need only the oid, I got rid of t1. If you need more fields from t1, then add JOIN t1 WHERE t1.oid = a.fk_oid and reference the fields needed.
(I am deleting my other Answer.)
I have two tables requisition_headers and order_headers. I am interested in finding the average time it takes from the time the requisition is submitted (requisition_headers.submitted_at) and the time the order is created (orders.headers_created_at) where the requisition_headers.status <> 'draft'.
I would like the result to look like:
Avg_Req_To_PO_Cycle_Time = 3.2 Days
I have the following script but it's not working:
SELECT Database() as Customer,
AVG(timestampdiff(requisition_headers.submitted_at,order_headers.created_at)) AS REQ_PO_Cycle_Time
FROM order_headers
LEFT JOIN requisition_headers ON order_headers.requisition_header_id = requisition_headers.id
WHERE requisition_headers.status <> 'draft'
Any Ideas?
--UPDATE--
I changed the query to the following and now get a response of 229491.71 my question is- is that days, hours, minutes, seconds?
SELECT DATABASE() AS CUSTOMER,
AVG(TIME_TO_SEC(TIMEDIFF(order_headers.created_at,requisition_headers.submitted_at))) as Cycle_time
FROM order_headers LEFT JOIN requisition_headers ON order_headers.requisition_header_id = requisition_headers.id
where requisition_headers.status <> 'draft'
Make sure you know what a system function returns when you use it. The TimestampDiff function returns the difference between the two dates in the unit you specify in the first argument. You don't specify that unit so I don't know what you get back. I get a compile error.
In your second attempt, you are using TimeDiff which returns an interval value, then converting the result of Avg to seconds. So if you want the result in fractional days just divide by the number of seconds in a day.
You also use a left join when getting the dates. At first I thought you wanted to get all the requisitions whether the orders had been created or not. But you are joining the tables in the wrong order for that. But, assuming that is your intention, if the order has not yet been created you will be putting NULL as one of the parameters. You will get a NULL as an answer so you get nothing. If you want to use a left join, then you should specify a substitute date for any missing Created dates -- after getting the table in the right order, that is.
Here are two options. One ignores orders that have not yet been created by using a regular inner join. The other includes those but substitutes the current date and time.
By asking for the number of minutes between the dates, the final answer in days is found by dividing by the number of minutes in a day.
SQLFiddle
SELECT Customer,
AVG( timestampdiff( minute, r.submitted_at,
o.created_at)) / (24 * 60 )AS REQ_PO_Cycle_Time
FROM requisition_headers r
JOIN order_headers o
ON o.requisition_header_id = r.id
WHERE r.status <> 'draft'
group by Customer;
SELECT Customer,
AVG( timestampdiff( minute, r.submitted_at,
IfNull( o.created_at, CurDate()))) / (24 * 60 )AS REQ_PO_Cycle_Time
FROM requisition_headers r
LEFT JOIN order_headers o
ON o.requisition_header_id = r.id
WHERE r.status <> 'draft'
group by Customer;
I have a table which contains all orders, i'm trying to separate orders by hour.
if there is no record for an specific hour, the query will ignore that hour, but what i'm trying to achieve is to report'0' for that hour.
I also joined the table with a temporary table containing all hours.
SELECT sum(orders.price), hour(orders.time) as hour
FROM orders
RIGHT JOIN dummy_time as dummy
ON hour(orders.time) = dummy.time
WHERE state = 1
AND (date(orders.time) = '2014-06-17' or orders.time is null)
GROUP BY hour
You can view my query in SQLFiddle
To get all rows from dummy_time, move your conditions from your WHERE to your RIGHT JOIN. Also, select the hour from dummy.time so you will get all hours.
Use COALESCE to get values of 0 where an order doesn't have records.
SELECT COALESCE(sum(orders.price),0), dummy.time as hour
FROM orders
RIGHT JOIN dummy_time as dummy
ON hour(orders.time) = dummy.time
AND orders.state = 1
AND orders.time BETWEEN '2014-06-17 00:00:00' AND '2014-06-17 23:59:59'
GROUP BY dummy.time
http://www.sqlfiddle.com/#!2/c7adb/2
The query plan for the query below looks worse than the one above but because you reported that the JOIN seems to be the main source of slowness it's worth a try. The query below reduces the set of rows before doing a JOIN.
SELECT
COALESCE(t1.orders_sum,0),
t2.time
FROM
(
SELECT
sum(orders.price) orders_sum,
hour(orders.time) orders_hour
FROM orders
WHERE orders.state = 1
AND orders.time BETWEEN '2014-06-17 00:00:00' AND '2014-06-17 23:59:59'
GROUP BY hour(orders.time)
) t1 RIGHT JOIN dummy_time t2 ON t1.orders_hour = t2.time
http://www.sqlfiddle.com/#!2/0775b/1
Also, make sure your tables are indexed
CREATE INDEX test_index1 ON orders (state,time);
CREATE INDEX test_index2 ON dummy_time (time);
Is this what you are looking for? It uses a case for when state=1 versus state=0 on whether to display sum orders or whether to display 0. If not please let me know your desired result.
I have a problem with a query which should do the following:
Get specific records and calculate some of their values for specific period of time
Calculate same values of these keywords for another period of time
This should happen into 1 query. I was able to write it but SUM() returns wrong value much higher than normal. I think that this is because of LEFT JOIN.
SELECT SQL_CALC_FOUND_ROWS table1.id, table1.KeywordId, table1.AccountName, table1.CampaignName, table1.AdGroupName, table1.Keyword, table1.MatchType, SUM(table1.Spend)/SUM(table1.Clicks) AS AverageCpc, SUM(table1.Impressions) AS Impressions, (SUM(table1.Clicks)*table1.revenue_price)/SUM(table1.Impressions) AS Ctr, SUM(table1.Impressions*table1.AveragePosition)/SUM(table1.Impressions) AS AveragePosition, SUM(table1.Clicks) AS Clicks, SUM(table1.Spend) AS Spend, SUM(table1.free_joins) AS FreeJoins, SUM(table1.paid_joins) AS PaidJoins, SUM(table1.paid_joins)*table1.revenue_price AS Revenue, (SUM(table1.paid_joins)*table1.revenue_price)-SUM(table1.Spend) AS Profit, (SUM(table1.paid_joins)*table1.revenue_price)/SUM(table1.Clicks) AS RevPerClick, table1.CurrentMaxCpc, SUM(table2.Impressions) AS Impressions_chg, SUM(table2.Clicks) AS Clicks_chg, SUM(table2.Impressions*table2.AveragePosition)/SUM(table2.Impressions) AS AveragePosition_chg, (SUM(table2.Clicks)*table2.revenue_price)/SUM(table2.Impressions) AS Ctr_chg, SUM(table2.Spend)/SUM(table2.Clicks) AS AverageCpc_chg, table2.CurrentMaxCpc as CurrentMaxCpc_chg, SUM(table2.free_joins) AS FreeJoins_chg, SUM(table2.paid_joins) AS PaidJoins_chg
FROM keywords_stats_google_naughtymeetings as table1
LEFT JOIN keywords_stats_google_naughtymeetings as table2
ON table1.keywordId = table2.keywordId
WHERE table1.timeperiod >= '2012-05-21 00:00:00' and table1.timeperiod <= '2012-05-27 00:00:00'
AND table2.timeperiod >= '2012-05-14' and table2.timeperiod <= '2012-05-20'
GROUP BY table1.KeywordId, table1.MatchType, table1.revenue_price, table2.KeywordId, table2.MatchType, table2.revenue_price
ORDER BY FreeJoins
asc
LIMIT 0, 10
Can someone give me an advice how I can get correct SUM results?
I think you need INNER JOIN here. Try to replace the LEFT JOIN with INNER JOIN.
P.S.
I don't get exactly what you want, but I think this idea should be simpler.
(SELECT id, fields_for_first_period_of_time
FROM keywords_stats_google_naughtymeetings) t1
JOIN
(SELECT id, fields_for_second_period_of_time
FROM keywords_stats_google_naughtymeetings) t2
ON t1.id = t2.id
This is just a sketch of the idea. I mean get the results with two separate queries. And then join them. This will be easier for debugging. I hope this can help you.
I am attempting to find all entries in the 'archive' table that have the same workID as any rows where historyID is NULL, worktype = "WEST", and the completeDate at least four months old. I am using a sub query to complete this request. Is there a more efficient way? My query is below.
SELECT * from archive b WHERE b.workID IN
(SELECT a.workID FROM archive a, worktypes t
WHERE a.historyID IS NULL AND (t.worktype IN ('WEST') AND a.worktype = t.id)
AND a.completeDate >= DATE(NOW() - Interval 4 month));
Additional Info: The tables are related only by the worktype. Basically I'm using the worktypes table just so I can type 'WEST' instead of giving an id number so it's easier to understand the query at-a-glance. I added on to the query. I didn't copy and paste it all. Oops.
Thank you!
SELECT b.*
FROM archive b
INNER JOIN archive a
on b.workID = a.workID
AND a.historyID IS NULL
AND a.completeDate >= DATE(NOW() - Interval 4 month)
INNER JOIN worktypes t
ON a.worktype = t.id
AND t.worktype = 'WEST'
The problem with your query and the proposed solution is that we don't know how the tables relate to each other. What column do you have in archive that is also in worktypes?
Also you should avoid "IN" where possible, it is inefficient.
Yeah sure:
SELECT * from archive a LEFT JOIN worktypes t ON a.workID=t.workID WHERE a.historyID IS NULL AND t.worktype IN ('WEST') AND a.completeDate >= DATE(NOW() - Interval 4 month));
The LEFT JOIN will get all the entries in the first table and appends to them the equivalent columns from the matched second table.
Hopes this helps.