I need to get all records between two week numbers. This is the query I'm using right now:
SELECT huxwz_user_orders . * , huxwz_users.name, huxwz_users.email FROM huxwz_user_orders
LEFT OUTER JOIN huxwz_users ON ( huxwz_user_orders.userid = huxwz_users.id )
WHERE
(STATUS=3 or STATUS=2)
AND plannedweek > 0
AND plannedweek >= WEEK(DATE_ADD(now(), interval 1 WEEK))
AND plannedweek < WEEK(DATE_ADD(now(), interval 3 WEEK))
AND NOT plannedweek=0
The query works very well and returns all records between the two provided weeks. HOWEVER When I add like 16 to the < ending of the query. MySQL interprets the 16 weeks should be added to the current week (42), So it's 16+42 = 58. Since we dont have 58 weeks and only 52, it must be the sixth week. This is exactly how it should do.
Problem is, when I want to find all records between two values and it does like above. It's going to look like this:
Get all records that are > 42 and < 16. This makes no sense, and it will obviously not return anything.
So, my records have a year, so I do know what year it is. I was thinking something along the lines: If the week, is next year, add 52 to the value. This is unreliable though, since not every year has 52 weeks.
How would I do this?
Any suggestions / solutions?
It looks like #Strawberry's comment is on the right track.
I corrected Strawberry's omission of the DATE_ADD() function, plus we need to adapt the value of planned week to match the results of the YEARWEEK() function.
e.g.:
SELECT huxwz_user_orders . * , huxwz_users.name, huxwz_users.email FROM huxwz_user_orders
LEFT OUTER JOIN huxwz_users ON ( huxwz_user_orders.userid = huxwz_users.id )
WHERE
(STATUS=3 or STATUS=2)
AND plannedweek > 0
AND (YEAR( NOW() ) * 100) + plannedweek >= YEARWEEK( WEEK(DATE_ADD(now(), interval 1 WEEK)), 1)
AND (YEAR( NOW() ) * 100) + plannedweek < YEARWEEK( WEEK(DATE_ADD(now(), interval 3 WEEK)), 1)
AND NOT plannedweek=0
If this doesn't work then your question may need some further clarification.
Related
set #cumulativeSum := 0;
SELECT
z.hour+1 as time,
(#cumulativeSum := #cumulativeSum + z.enquiries) as target
FROM
( SELECT
y.hour as hour,
IFNULL(ROUND(AVG(y.enquiries), 0), 0) as enquiries
FROM
( SELECT DAY(o.date_created),
HOUR(o.date_created) as hour,
COUNT(DISTINCT o.phone) as enquiries
FROM orders o
WHERE phone IS NOT NULL
AND name NOT LIKE 'test%'
AND o.email NOT LIKE 'jawor%'
AND o.email NOT LIKE 'test%'
AND o.email NOT LIKE '%moneyshake%'
AND o.date_created < CURRENT_DATE()
AND o.date_created >= DATE(NOW()) + INTERVAL -7 DAY
GROUP BY HOUR(o.date_created), DAY(o.date_created) ) y
GROUP BY hour ) z
This query is meant to give an average number of enquiries by hour for the last 7 days. I've done it this way to exclude duplicates of o.phone only within each hour of each day, rather than across all days or all hours.
It outputs:
time
target
1
1
2
2
3
3
5
4
etc..
etc..
I want it to include a row for 4am, even if the value for target doesn't change (because the AVG for 4am is 0)
Please let me know if more info is needed!
Credit to Akina's comment for solving
We have multiple invStatus values (1-10) and want to exclude only one status type (1) BUT only those of that type that are a older than X number of days. So all records will show but NOT those who's invStatus = 1 and is older than X days. invStatus = 1 and younger than X days will be included in the recordset.
Do I select all records generically, then in a subquery filter those of status = 1 that are older than X days?
The query below uses NOT IN in an attempt to select those records to exclude but it is not working and also seems to be inefficient as it takes a couple seconds to execute.
SELECT
tblinventory.invId,
tblinventory.invTitle,
tblinventory.invStatus,
tblhouseinfo.Address,
tblhouseinfo.City,
tblhouseinfo.`State`,
tblhouseinfo.Zip,
tblhouseinfo.Update_date,
CURRENT_DATE() - INTERVAL 10 DAY AS dateEx
FROM
tblinventory
LEFT OUTER JOIN tblhouseinfo ON tblinventory.invId = tblhouseinfo.addInfoID
WHERE
invReleased = 0
AND invStatus NOT IN (SELECT invId from tblhouseinfo WHERE invStatus = 1
AND tblhouseinfo.Update_date < CURRENT_DATE() - INTERVAL 10 DAY )
ORDER BY
`tblhouseinfo`.`Update_date` DESC
I could filter the results with PHP on the page level but this also seems less than efficient and would prefer to perform this task using the best practices.
UPDATE:
There are a total of 155 rows.
All tblhouseinfo.Update_date (timestamp) values are "2017-09-06 10:53:17" (Aug 9th) accept three I changed for testing to "2017-07-06 10:53:17
" (July 6th)
Utilizing the suggestion for :
AND NOT (invStatus = 1 AND tblhouseinfo.Update_date > CURRENT_DATE() - INTERVAL 10 DAY )
60 records are excluded not the expected 3.
"2017-08-28" is the current result from CURRENT_DATE() - INTERVAL 10 DAY which should be within the 10 day range to select "2017-09-06 10:53:17" and only exclude the three records that are "2017-07-06 10:53:17"
FINAL WORKING SOLUTION/Query:
SELECT
tblinventory.invId,
tblinventory.invTitle,
tblinventory.invStatus,
tblhouseinfo.Address,
tblhouseinfo.City,
tblhouseinfo.`State`,
tblhouseinfo.Zip,
tblhouseinfo.Update_date,
CURRENT_DATE() - INTERVAL 10 DAY AS dateEx
FROM
tblinventory
LEFT OUTER JOIN tblhouseinfo ON tblinventory.invId = tblhouseinfo.addInfoID
WHERE
invReleased = 0
AND NOT (invStatus = 1 AND tblhouseinfo.Update_date < CURRENT_DATE() - INTERVAL 10 DAY )
ORDER BY
`tblhouseinfo`.`Update_date` DESC
SELECT
tblinventory.invId,
tblinventory.invTitle,
tblinventory.invStatus,
tblhouseinfo.Address,
tblhouseinfo.City,
tblhouseinfo.`State`,
tblhouseinfo.Zip,
tblhouseinfo.Update_date,
CURRENT_DATE() - INTERVAL 10 DAY AS dateEx
FROM
tblinventory
LEFT OUTER JOIN tblhouseinfo ON tblinventory.invId = tblhouseinfo.addInfoID
WHERE
invReleased = 0
AND NOT (invStatus = 1 AND tblhouseinfo.Update_date < CURRENT_DATE() - INTERVAL 10 DAY )
ORDER BY
`tblhouseinfo`.`Update_date` DESC
You don't need to select invID from the other table if you know you never want the ID #1 (invStatus 1). But you can also throw in an AND statement for the # of days.
I always use timestamps (in UNIX) for recording data entry / modification.
AND (timestamp >= beginTimestamp AND timeStamp <= endTimestamp)
I am trying to make a report. It is supposed to give me a list of the machines at a specific customer and the sum of hours and material that was put in to that machine.
In the following examples, I select the sum of materials and hours in different fields to make the problem clearer. But i really want to sum the material an hours, then group them by the machine field.
I can query the list of machine and cost of hours without problems.
SELECT CONCAT(`customer`.`PREFIX`, `wo`.`machine_id`) AS `machine`,
ROUND(COALESCE(SUM(`wohours`.`length` * `wohours`.`price`), 0), 2) AS `hours`
FROM `wo`
JOIN `customer` ON `customer`.`id`=`wo`.`customer_id`
LEFT JOIN `wohours` ON `wohours`.`wo_id`=`wo`.`id` AND `wohours`.`wo_customer_id`=`wo`.`customer_id`
AND `wohours`.`wo_machine_id`=`wo`.`machine_id` AND `wohours`.`date`>=(CURDATE() - INTERVAL DAY(CURDATE() - INTERVAL 1 DAY) DAY) - INTERVAL 11 MONTH
WHERE `wo`.`customer_id`=1
GROUP BY `wo`.`machine_id`;
This gives me the correct values for hours. But when I add the material like this:
SELECT CONCAT(`customer`.`PREFIX`, `wo`.`machine_id`) AS `machine`,
ROUND(COALESCE(SUM(`wohours`.`length` * `wohours`.`price`), 0), 2) AS `hours`,
ROUND(COALESCE(SUM(`womaterial`.`multiplier` * `womaterial`.`price`), 0), 2) AS `material`
FROM `wo`
JOIN `customer` ON `customer`.`id`=`wo`.`customer_id`
LEFT JOIN `wohours` ON `wohours`.`wo_id`=`wo`.`id` AND `wohours`.`wo_customer_id`=`wo`.`customer_id`
AND `wohours`.`wo_machine_id`=`wo`.`machine_id` AND `wohours`.`date`>=(CURDATE() - INTERVAL DAY(CURDATE() - INTERVAL 1 DAY) DAY) - INTERVAL 11 MONTH
LEFT JOIN `womaterial` ON `womaterial`.`wo_id`=`wo`.`id` AND `womaterial`.`wo_customer_id`=`wo`.`customer_id`
AND `womaterial`.`wo_machine_id`=`wo`.`machine_id` AND `wohours`.`date`>=(CURDATE() - INTERVAL DAY(CURDATE() - INTERVAL 1 DAY) DAY) - INTERVAL 11 MONTH
WHERE `wo`.`customer_id`=1
GROUP BY `wo`.`machine_id`;
then both hour and material values are incorrect.
I have read other threads where people with similar problems could solve this by splitting it in multiple queries or subqueries. But I don't think that is possible in this case.
Any help is appreciated.
//John
Your other reading is correct. You will need to put them into their own "subquery" for the join. The reason you are probably getting invalid values is that the materials table has multiple records per machine, thus causing a Cartesian result from your original based on hours. And you don't know which has many vs just one making it look incorrect.
So, I've written, and each inner-most query for pre-aggregating the woHours and woMaterial will produce a single record per "wo_id and machine_id" to join back to the wo table when finished. Each of these queries has the criteria on the single customer ID you are trying to run it for.
Then, as re-joined to the work order (wo) table, it grabs all records and applies the ROUND() and COALESCE() in case no such hours or materials present. So this is a return of something like
WO Machine ID Machine Hours Material
1 1 CustX 1 2 0
2 4 CustY 4 2.5 6.5
3 4 CustY 4 1.2 .5
4 1 CustX 1 1.5 1.2
Finally, you can now roll up the SUM() of all these entries into a single row per machine ID
Machine Hours Material
CustX 1 3.5 1.2
CustY 4 3.7 7.0
SELECT
AllWO.Machine,
SUM( AllWO.Hours ) Hours,
SUM( AllWO.Material ) Material
from
( SELECT
wo.wo_id,
wo.Machine_ID,
CONCAT(customer.PREFIX, wo.machine_id) AS machine,
ROUND( COALESCE( PreSumHours.MachineHours, 0), 2) AS hours,
ROUND( COALESCE( PreSumMaterial.materialHours, 0), 2) AS material
FROM
wo
JOIN customer
ON wo.customer_id = customer.id
LEFT JOIN ( select wohours.wo_id,
wohours.wo_machine_id,
SUM( wohours.length * wohours.price ) as machinehours
from
wohours
where
wohours.wo_customer_id = 1
AND wohours.date >= ( CURDATE() - INTERVAL DAY( CURDATE() - INTERVAL 1 DAY) DAY) - INTERVAL 11 MONTH
group by
wohours.wo_id,
wohours.wo_machine_id ) as PreSumHours
ON wo.id = PreSumHours.wo_id
AND wo.machine_id = PreSumHours.wo_machine_id
LEFT JOIN ( select womaterial.wo_id,
womaterial.wo_machine_id,
SUM( womaterial.length * womaterial.price ) as materialHours
from
womaterial
where
womaterial.wo_customer_id = 1
AND womaterial.date >= ( CURDATE() - INTERVAL DAY( CURDATE() - INTERVAL 1 DAY) DAY) - INTERVAL 11 MONTH
group by
womaterial.wo_id,
womaterial.wo_machine_id ) as PreSumMaterial
ON wo.id = PreSumMaterial.wo_id
AND wo.machine_id = PreSumMaterial.wo_machine_id
WHERE
wo.customer_id = 1 ) AllWO
group by
AllWO.Machine_ID
Suppose you have a table of the form:
create table user_activity (
user_id int not null,
activity_date timestamp not null,
...);
It's easy enough to select the number of unique user_id's in the past 30 days.
select count(distinct user_id) from user_activity where activity_date > now() - interval 30 day;
But how can you select the number of unique user_ids in the prior 30 days for each of the past 30 days? E.g. uniques for 0-30 days ago, 1-31 days ago, 2-32 days ago and so on to 30-60 days ago.
The database engine is mysql if it matters
You could try using a sub query:
SELECT DISTINCT `activity_date` as `day`, (
SELECT count(DISTINCT `user_id`) FROM `user_activity` WHERE `activity_date` = `day`
) as `num_uniques`
FROM `user_activity`
WHERE `activity_date` > NOW() - INTERVAL 30 day;
This should give you the number of unique users for each day. However, I haven't tested this since I don't have the DB to work with.
I haven't tried this in MySQL, but hopefully the syntax is right. If not, maybe it will point you in the right direction. First, I often employ a Numbers table. It can be a physical table simply made up of numbers or it can be a generated/virtual/temporary table.
SELECT
N.number,
COUNT(DISTINCT UA.user_id)
FROM
Numbers N
INNER JOIN User_Activity UA ON
UA.activity_date > NOW() - INTERVAL 30 + N.number DAY AND
UA.activity_date <= NOW() - INTERVAL N.number DAY
WHERE
N.number BETWEEN 0 AND 30
GROUP BY
N.number
I'm not familiar with the whole INTERVAL syntax, so if I got that wrong, please let me know and I'll try to correct it.
If you get the days number for todays date and mod it by 30 you get the offset of the current day. Then you add that to each number for a date and divide the result by 30, this gives you the group of days. Then group your results by this number. So in code something like this:
select count(distinct user_id), (to_days(activity_date)+(to_days(now()) % 30)) / 30 as period
from user_activity
group by (to_days(activity_date)+(to_days(now()) % 30)) / 30
I will leave calculating the reverse numbering of period up to you (hint: take the period number for the current date as "max" and subtract period above and add 1.)
I have a datetime column called 'last_login'.
I want to query my database to select all records that haven't logged in within the last month. How do I do this?
This is what I have currently:
$query = $this->query("SELECT u.id, u.name, u.email, u.registered, g.name as group_name FROM `:#users` AS u LEFT JOIN `:#groups` AS g on u.group_id = g.id WHERE u.last_login = ...... LIMIT {$limit_start}, {$limit_end}");
:# = database prefix
Try using date_sub
where u.last_login < date_sub(now(), interval 1 month)
(Similar to the first answer but in my mind it is more "natural" to use positive integers)
You can use date_add combined with now:
...where u.last_login < date_add(now(), interval -1 month)
Naturally, as both are MySQL-specific this limits you to MySQL backends. Alternately, you can figure out what the date was a month ago with PHP (I'm not a PHP person, but I'm guessing DateTime::sub would help with that) and then include that date in your query in the normal way you would any other date/time field.
matthewh was almost correct, except the > should have been a right one.
where u.last_login > date_sub(now(), interval 1 month)