I have a $wpdb query I'm trying to execute, but it's not going through and is throwing no error:
$followups =
$wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM orders
WHERE status_id = %d
AND DATEDIFF(CURDATE(), date_waiting_pickup) % 7 = %d",
array(66, 0)
)
);
Any idea why? It runs fine in Terminal / direct MySQL. Is it the DIFFDATE() function?
EDIT: And interestingly enough, if I remove the $wpdb->prepare function, and leave $wpdb->get_results(), it works fine. So is there something I'm missing as far as how $wpdb->prepare() works?
In case that you want, as it seems, orders in the last week... Why don't you simplify it? There is no need for the second %d just put 0.
$followups =
$wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM orders
WHERE status_id = %d
AND DATEDIFF(CURDATE(), date_waiting_pickup) % 7 = 0",
66
)
);
UPDATE:
#dtj You are using mod (%) operator that give us the remainder of the division. So comparing with 0 (my first assumption) we obtain orders from today, 1 week ago, 2 week ago, etc.. If we compare with 1 (in the second %d) we obtain orders form yesterday, 8 days ago, etc...
Is really that what you want?
UPDATE 2: In your edited question you say that removing $wpdb->prepare() all works fine. No doubt is interesting, but fortunately you really don't need prepare() if you just works with integer numbers. I mean doing:
$status_id = (int) (is_numeric($status_id) ? $status_id : 0);
$followups = $wpdb->get_results("
SELECT * FROM orders
WHERE status_id = $status_id
AND DATEDIFF(CURDATE(), date_waiting_pickup) % 7 = 0");
you assure a safe query and gain in simplicity and efficiency. In my case I only use prepare() if there are strings involved and always test integer numbers as showed.
Related
I would like to dynamically search for a date in my database according to my $data input.
Dates are stored as date-times and are usually displayed as ex: "2016-05-11".
With a MySql DB my clause was:
Select * From Table WHERE ( Table.date LIKE '$data%' )
So if the user enters "20" the results will be all dates that start with 20. If the user enters "2016-05" the results will be all dates that start with 2016-05, making my search perfectly working.
When deploying over a Sql Server 2008. I realized 2 things.
1) If i use the LIKE function as Such
Select * From Table WHERE ( Table.date LIKE '%$data$%')
The function will work for a value of "2016" , but would return 0 results when a value of "2016-" is entered.
2) If i use the function DATETIME as such
Select * From Table WHERE ( DATEPART(year, Table.date) LIKE(OR)= '$data')
The function will return correct values when $data = 2016, but will return nil values when the $data = '201' for example, hence making it not dynamic.
What am i doing wrong?
Implicit conversion is just bad, bad, bad -- in SQL or most other programming languages.
LIKE acts on strings. If you want it to work on strings, then explicitly make sure that the parameters are strings. This is true in MySQL and SQL Server.
If you want to use LIKE on the year, then use DATENAME() rather than DATEPART():
WHERE DATENAME(year, Table.date) LIKE '$data'
However, I would encourage you to think about dates using date-based constructs, such as date ranges.
I just realized my stupid mistake.
I needed to add a wildcard to my LIKE clause using DATEPART as such.
Select * From Table WHERE ( DATEPART(year, Table.date) LIKE = '$data%' ) .
Works perfectly.
But i would love other suggestions to simplify splitting $data into YYYY , MM , DD and then testing each element.
I would strive to keep Table.date free of manipulation, so indexes are used.
Instead you can use a CASE expression to convert the search parameter to a date range by adding the missing parts:
Lower bound:
CASE LENGTH($data)
WHEN 8 THEN $data + '01'
WHEN 7 THEN $data + '-01'
WHEN 5 THEN $data + '01-01'
WHEN 4 THEN $data + '-01-01'
ELSE $data
END
Upper bound:
CASE LENGTH($data)
WHEN 8 THEN DATEADD(MONTH,$data + '01',1)
WHEN 7 THEN DATEADD(MONTH,$data + '-01',1)
WHEN 5 THEN DATEADD(YEAR,$data + '01-01')
WHEN 4 THEN DATEADD(YEAR,$data + '-01-01')
ELSE DATEADD(DAY,$data,1)
END
In my users table I have a field hours, and when I'm selecting all my users I need to also get a variable fullTime which will be based on if hours equals 7.4. Is this something I can do in a query rather than having to loop through all the users? I have 600+ rows and I'm trying to do this the most efficient way as this happens on every page load. I was thinking something like SELECT *, fullTime as (hours EQUALS 7.4) FROM users
EDIT: Working SQL SELECT * , IF(hours_per_day = 7.5, 1, 0) AS fullTime FROM users
The following will return a column with fulltime = 1 if hours = 7.4 and fulltime = 0 otherwise.
SELECT *, IF(hours = 7.4, 1, 0) AS fulltime FROM users
Note that this may fail if hours is a float column due to inaccuracies in floating point numbers.
try this
SELECT * , if(hours >= 7.4 ,col_time, hours) as fullTime FROM users
explanation :
if hours >=7.4 (not only exactly hours=7.4 but also greater.) you will get your column where you stored the full date with time or whole time. you can even change it by DATE_FORMAT. otherwise you will get the hours lets say 5 ,or something.
I'm making a query to monitor ongoing and expecting file uploads.
Here is what i'm making:
select time_to_sec(min(timediff(now(), f.SubmittedDate)))/60 FROM dbtable f WHERE f.Status = 'SUBMITTED';
Now, after 12 hours, this keeps returning 10 times more then it should. Instead of 620 minutes, it returns 6200 minutes. If i do it this way, it works:
select time_to_sec(timediff(now(), max(f.SubmittedDate)))/60 FROM dbtable f WHERE f.Status = 'SUBMITTED'
I don't understand the difference, why is this happening? Obviously, the lowest timediff should be equal to the timediff between now and the highest date. Am i missing something or is this a bug?
My problem is that i have loads of checks set up this way and editing all of them could be a fair share amount of work.
Thanks for any help!
I ran some tests on this and your code:
select time_to_sec(min(timediff(now(), f.SubmittedDate)))/60 FROM dbtable f WHERE f.Status = 'SUBMITTED';
Needs to be:
select min(time_to_sec(timediff(now(), f.SubmittedDate)))/60 FROM dbtable f WHERE f.Status = 'SUBMITTED';
As you aren't using the "MAX" function like in your second query you need to call the "MIN" function before the "TIME_TO_SEC".
Hope this helps
I am building a webservice using php & mysql, and would like to limit requests from each apikey to (x) amount within (y) time.
To achieve this, i count the records added to the the log from each apikey within a period using a sql statement like this:
SELECT COUNT(*) FROM ws_account_log WHERE account_log_account_id='1' AND account_log_timestamp > DATE_SUB(now(), INTERVAL 1 HOUR)
This gives me the a rolling hourly count, which is fine, but I would like a hard limit on the monthly count. eg how many rows have been added since 00:00 of the first day of the current month.
I have seen examples using stored procedures and different syntax, I would like an answer that will work on MYSQL please. Also, if possible the fastest implementation as one of the services provides autocomplete functions, therefore the log table is rather large.
Thanks
SELECT COUNT(*)
FROM ws_account_log
WHERE
account_log_account_id='1'
AND account_log_timestamp >= SUBDATE(CURDATE(), DAYOFMONTH(CURDATE())-1)
Calculate timestamp to the moment you want using php, then pass it to mysql query.
$date = mktime(0, 0, 0, date("n"), 1, date("Y"));
$mysqldate = date( 'Y-m-d H:i:s', $date );
$query = " ... AND account_log_timestamp > ".$mysqldate;
Query 1 works but query 2 doesn't:
Query #1:
SELECT * FROM `users` WHERE users.dob <= '1994-1-14' AND users.dob >= '1993-1-14' LIMIT 10
Query #2:
SELECT * FROM `users` WHERE users.dob BETWEEN '1994-1-14' AND '1993-1-14' LIMIT 10
The 2nd one should be able to do the same thing as the first but I don't understand why it's not working.
The dob (date of birth) field in the users table is a type date field with records that look like this:
1988-11-08
1967-11-14
1991-03-09
1958-03-08
1967-06-30
1988-10-19
1986-01-23
1965-09-20
YEAR - MONTH - DAY
With either query #1 or #2 I'm trying to get back all users who are between 18 and 19 years of age, because 1994-1-14 is exactly 18 years from today and 1993-1-14 is 19 years from today. So is there a way to get the between query to work?
By not working I mean it doesn't return any records from the db while the working query does.
Also is the between query more efficient or is the performance difference negligible?
To answer the first part: "expr BETWEEN min AND max". Try switching those 2 dates in the second query.
The usage is wrong. See the BETWEEN documentation:
expr BETWEEN min AND max is equivalent to (min <= expr AND expr <= max).
Therefore, users.dob BETWEEN '1994-1-14' AND '1993-1-14' is the same as ('1994-1-14' <= users.dob AND users.dob <= '1993-1-14'), of which there will never be more than 0 results.
Simply reverse the order :)
There will be no performance difference when using either form, possibly subject to the note below. This transformation happens at the query planner level. However, if you have concerns, remember to profile, profile, profile. Then you can see for yourself and appease the premature-optimization demons.
Also note the ... note:
For best results when using BETWEEN with date or time values, use CAST() to explicitly convert the values to the desired data type.