How do I use mysql variables with php mysql extension? - php

I'm doing this query from any mysql client, and it's working:
SELECT
q.calldate,
q.dst,
#ts_dnd_activate := IF(q.dst='*78',q.calldate,#ts_dnd_activate) AS dndact,
#ts_dnd_deactivate := IF(q.dst='*79',q.calldate,NULL) AS dnddeact,
TIMESTAMPDIFF(SECOND,#ts_dnd_activate,#ts_dnd_deactivate) AS tdiff
FROM
(SELECT calldate,dst
FROM cdr
WHERE calldate>='2012-01-09 11:12:08' AND src=1004 AND dst IN ('*78','*79')
ORDER BY calldate DESC) q
It gets two fields from a table and, based on the value of the dst field, uses mysql variables to calculate the difference between the calldates fields, which is a timestamp.
If I run this query in PHP code:
[...]
$q = "SELECT
q.calldate,
q.dst,
#ts_dnd_activate := IF(q.dst='*78',q.calldate,#ts_dnd_activate) AS dndact,
#ts_dnd_deactivate := IF(q.dst='*79',q.calldate,NULL) AS dnddeact,
TIMESTAMPDIFF(SECOND,#ts_dnd_activate,#ts_dnd_deactivate) AS tdiff
FROM
(SELECT calldate,dst
FROM cdr
WHERE calldate>='2012-01-09 11:12:08' AND src=1004 AND dst IN ('*78','*79')
ORDER BY calldate DESC) q";
$rs = mysql_query($q,$database);
[...]
I'll never get the tdiff field, because I'm getting rows where the mysql variables are always NULL, like this:
Array
(
[calldate] => 2012-01-09 12:20:10
[dst] => *78
[dndact] => 2012-01-09 12:20:10
[dnddeact] =>
[tdiff] =>
)
Array
(
[calldate] => 2012-01-09 12:22:05
[dst] => *79
[dndact] =>
[dnddeact] => 2012-01-09 12:22:05
[tdiff] =>
)
Array
(
[calldate] => 2012-01-09 12:25:15
[dst] => *78
[dndact] => 2012-01-09 12:25:15
[dnddeact] =>
[tdiff] =>
)
Array
(
[calldate] => 2012-01-09 12:29:21
[dst] => *79
[dndact] =>
[dnddeact] => 2012-01-09 12:29:21
[tdiff] =>
)
Please note that it is a SINGLE query, but it seems that it's impossible to use mysql variables with the php mysql base extension because I don't know why but it seems to set to null every row.
What I'm doing wrong? Is it a new problem?
P.S. please don't tell me I've to use PDO or mysqli; its an old project that I can't rewrite.

Related

Order by not working with group by clause

I am using corequery of mysql in cakephp. I want the records in descending order. This is my table structure
enter code here
$coreQueryUser = $this->Message->query(
"select * from messages where messages.list_id = 3
group By (if(sender_id > reciever_id, sender_id, reciever_id)),
(if(sender_id > reciever_id, reciever_id, sender_id))
order by id desc
"
);
I want last message that belongs to (sender_id and reciver_id and viceversa) that belongs to list id 3
when i run this query i get the following output
<pre>Array
(
[0] => Array
(
[messages] => Array
(
[id] => 1
[sender_id] => 21
[reciever_id] => 10
[list_id] => 3
[message] => hello
[add_date] => 2016-09-25 00:00:00
[is_check] => 0
)
)
[1] => Array
(
[messages] => Array
(
[id] => 3
[sender_id] => 22
[reciever_id] => 10
[list_id] => 3
[message] => hello s
[add_date] => 2016-09-25 16:39:41
[is_check] => 0
)
)
)
but i wnat result like that:
Array
(
[0] => Array
(
[messages] => Array
(
[id] => 2
[sender_id] => 10
[reciever_id] => 21
[list_id] => 3
[message] => hello sir
[add_date] => 2016-09-25 00:00:00
[is_check] => 0
)
)
[1] => Array
(
[messages] => Array
(
[id] => 6
[sender_id] => 22
[reciever_id] => 10
[list_id] => 3
[message] => new
[add_date] => 2016-09-25 16:39:41
[is_check] => 0
)
)
)
Can anyone help me :(
The problem is that your query is against the sql standard because you have several fields in the select list that are neither in the group by list, nor are subject of an aggregate function, such as sum(). MySQL unfortunately allows such invalid queries to run under certain sql mode settings (the default settings of the most recent versions of MySQL would prevent such queries from running).
As MySQL documentation on group by clause says (bolding is mine):
If ONLY_FULL_GROUP_BY is disabled, a MySQL extension to the standard
SQL use of GROUP BY permits the select list, HAVING condition, or
ORDER BY list to refer to nonaggregated columns even if the columns
are not functionally dependent on GROUP BY columns. This causes MySQL
to accept the preceding query. In this case, the server is free to
choose any value from each group, so unless they are the same, the
values chosen are indeterminate, which is probably not what you want.
Furthermore, the selection of values from each group cannot be
influenced by adding an ORDER BY clause. Result set sorting occurs
after values have been chosen, and ORDER BY does not affect which
value within each group the server chooses.
You apparently want the latest record (with max(id) for each group. The proper way is to have a subquery that returns the max(id) per group and in the outer query join back to your main table using the ids to get the value of the other fields:
select m.*
from
messages m
inner join (
select max(id) as maxid
from messages
where messages.list_id = 3
group By (if(sender_id > reciever_id, sender_id, reciever_id)),
(if(sender_id > reciever_id, reciever_id, sender_id))
) t1 on m.id=t1.maxid
This code working:
SELECT * FROM generate_invoice
WHERE id IN
(
SELECT max(id) as id
FROM generate_invoice
GROUP by pay_id
ORDER by id DESC
)
How to group by DESC order
Try
SELECT * FROM ( SELECT * FROM generate_invoice ORDER BY id DESC ) AS g GROUP BY g.pay_id
OR
Use this code
SELECT m1.*,m2.* FROM generate_invoice m1 LEFT JOIN generate_invoice m2 ON (m1.pay_id = m2.pay_id AND m1.id < m2.id ) order by m1.id desc

Pushing pointers to followers with the metadata (MySQL Query)

I’ve seen the following question on StackOverflow, Intelligent MySQL GROUP BY for Activity Streams posted by Christian Owens 12/12/12.
So I decided to try out the same approach, make two tables similar to those of his. And then I pretty much copied his query which I do understand.
This is what I get out from my sandbox:
Array
(
[0] => Array
(
[id] => 0
[user_id] => 1
[action] => published_post
[object_id] => 776286559146635
[object_type] => post
[stream_date] => 2015-11-24 12:28:09
[rows_in_group] => 1
[in_collection] => 0
)
)
I am curious, since looking at the results in Owens question, I am not able to fully get something, and does he perform additional queries to grab the actual metadata? And if yes, does this mean that one can do it from that single query or does one need to run different optimized sub-queries and then loop through the arrays of data to render the stream itself.
Thanks a lot in advanced.
Array
(
[0] => Array
(
[id] => 0
[user_id] => 1
[fullname] => David Anderson
[action] => hearted
[object_id] => array (
[id] => 3438983
[title] => Grand Theft Auto
[Category] => Games
)
[object_type] => product
[stream_date] => 2015-11-24 12:28:09
[rows_in_group] => 1
[in_collection] => 1
)
)
In "pseudo" code you need something like this
$result = $pdo->query('
SELECT stream.*,
object.*,
COUNT(stream.id) AS rows_in_group,
GROUP_CONCAT(stream.id) AS in_collection
FROM stream
INNER JOIN follows ON stream.user_id = follows.following_user
LEFT JOIN object ON stream.object_id = object.id
WHERE follows.user_id = '0'
GROUP BY stream.user_id,
stream.verb,
stream.object_id,
stream.type,
date(stream.stream_date)
ORDER BY stream.stream_date DESC
');
then parse the result and convert it in php
$data = array(); // this will store the end result
while($row = $result->fetch(PDO::FETCH_ASSOC)) {
// here for each row you get the keys and put it in a sub-array
// first copy the selected `object` data into a sub array
$row['object_data']['id'] = $row['object.id'];
$row['object_data']['title'] = $row['object.title'];
// remove the flat selected keys
unset($row['object.id']);
unset($row['object.title']);
...
$data[] = $row; // move to the desired array
}
you should get
Array
(
[0] => Array
(
[id] => 0
[user_id] => 1
[fullname] => David Anderson
[verb] => hearted
[object_data] => array (
[id] => 3438983
[title] => Grand Theft Auto
[Category] => Games
)
[type] => product
[stream_date] => 2015-11-24 12:28:09
[rows_in_group] => 1
[in_collection] => 1
)
)
It seems that you want a query where you can return the data you're actually able to get plus the user fullname and the data related to the object_id.
I think that the best effort would be to include some subqueries in your query to extract these data:
Fullname: something like (SELECT fullname FROM users WHERE id = stream.user_id) AS fullname... or some modified version using the stream.user_id, as we can't identify in your schema where this fullname comes from;
Object Data: something like (SELECT CONCAT_WS(';', id, title, category_name) FROM objects WHERE id = stream.object_id) AS object_data. Just as the fullname, we can't identify in your schema where these object data comes from, but I'm assuming it's an objects table.
One object may have just one title and may have just one category. In this case, the Object Data subquery works great. I don't think an object can have more than one title, but it's possible to have more than one category. In this case, you should GROUP_CONCAT the category names and take one of the two paths:
Replace the category_name in the CONCAT_WS for the GROUP_CONCAT of all categories names;
Select a new column categories (just a name suggestion) with the subquery which GROUP_CONCAT all categories names;
If your tables were like te first two points of my answer, a query like this may select the data, just needing a proper parse (split) in PHP:
SELECT
MAX(stream.id) as id,
stream.user_id,
(select fullname from users where id = stream.user_id) as fullname,
stream.verb,
stream.object_id,
(select concat_ws(';', id, title, category_name) from objects where id = stream.object_id) as object_data,
stream.type,
date(stream.stream_date) as stream_date,
COUNT(stream.id) AS rows_in_group,
GROUP_CONCAT(stream.id) AS in_collection
FROM stream
INNER JOIN follows ON 1=1
AND stream.user_id = follows.following_user
WHERE 1=1
AND follows.user_id = '0'
GROUP BY
stream.user_id,
stream.verb,
stream.object_id,
stream.type,
date(stream.stream_date)
ORDER BY stream.stream_date DESC;
In ANSI SQL you can't reference columns not listed in your GROUP BY, unless they're in aggregate functions. So, I included the id as an aggregation.

multiple tables left join returns same value

Is it possible to make a query that will return one time the value of the second table and set the otherones at NULL. Im stuck with this.
This is my query
return $this->db->get_results(
"
SELECT id, name, type, check_in_days, check_out_days, all_check_out_days, minimum_stay, maximum_stay, all_accom, GROUP_CONCAT( accom_id ) as accom, GROUP_CONCAT( seasons_id ) as seasons, conditional_type
FROM $this->booking_rules_table
LEFT JOIN $this->booking_rules_accom_table
ON $this->booking_rules_table.id = $this->booking_rules_accom_table.rule_id
LEFT JOIN $this->booking_rules_seasons_table
ON $this->booking_rules_table.id = $this->booking_rules_seasons_table.rule_id
GROUP BY id
"
, ARRAY_A );
This returns a array like this
[2] => Array
(
[id] => 54
[name] =>
[type] => minimum_stay
[check_in_days] => 0,1,2,3,4,5,6
[check_out_days] => 0,1,2,3,4,5,6
[all_check_out_days] => 1
[minimum_stay] => 0
[maximum_stay] => 9999
[all_accom] => 0
[accom] => 7,7,7
[seasons] => 1,3,4
[conditional_type] => compulsory
)
You see that [accom] is returning the 7 3 times because the [seasons] has 3 values.
can i fix this with my query or is there an other solution. I dont want to explode it and build the array again.

Return a value not found in the table with MySQL?

Assuming the following array:
$users = array(
// User ID => Username
'72' => 'jack192',
'23' => 'robert1984',
'253' => 'mary111',
'4' => 'jason92'
);
and the following table:
Table myTable:
username | colFoo | colBar
I would like run a query like the following, however I would like the output to additionally include a column not in the table (The user's ID from the array):
$user_string = implode("','", array_values($users));
$query = "SELECT username, colFoo, colBar FROM myTable WHERE username IN ('$user_string')";
This would normally output something like this:
Array
(
[0] => Array
(
[username] => jack192
[colFoo] => 98
[colBar] => 7
)
[1] => Array
(
[username] => robert1984
[colFoo] =>
[colBar] => 2
)
[2] => Array
(
[username] => mary111
[colFoo] => 41
[colBar] => 9
)
[3] => Array
(
[username] => jason92
[colFoo] => 46
[colBar] => 13
)
)
However, I would like the output to look like this, with user_id corresponding to the key in the original array:
Array
(
[0] => Array
(
[username] => jack192
[colFoo] => 98
[colBar] => 7
[user_id] => 72
)
[1] => Array
(
[username] => robert1984
[colFoo] =>
[colBar] => 2
[user_id] => 23
)
[2] => Array
(
[username] => mary111
[colFoo] => 41
[colBar] => 9
[user_id] => 253
)
[3] => Array
(
[username] => jason92
[colFoo] => 46
[colBar] => 13
[user_id] => 4
)
)
I suppose I basically want to just feed the user's ID into the query and get it back out as output, without MySQL doing anything further with it. Is this possible to do purely in SQL without any additional PHP code?
Note: I do not have write access to the DB I'm pulling this data from, and I did not create the schema, so I can't add a user_id field to it or anything.
Here is another way to do it, almost a mashup of the other 2 answers. You can build a 'fake' table and JOIN it to myTable.
SELECT t.username, t.colFoo, t.colBar, s.user_id
FROM myTable t
LEFT JOIN
(
SELECT 72 as user_id, 'jack192' as username
UNION SELECT 23 as user_id, 'robert1984' as username
UNION SELECT 253 as user_id, 'mary111' as username
UNION SELECT 4 as user_id, 'jason92' as username
) s
ON t.username = s.username;
SQLFiddle example - sqlfiddle.com/#!2/64650/2
The fake table structure can be created by a php foreach loop.
$select = '';
foreach ($users as $k => $v){
$select .= "UNION SELECT $k as user_id, '$v' as username\n";
}
echo ltrim($select, "UNION ");
Still a tedious, and not ideal solution, but another option.
You might try CASE
SELECT username, colFoo, colBar,
CASE `username`
WHEN 'jack192' THEN SELECT 72;
WHEN 'robert1984' THEN SELECT 23;
ELSE SELECT NULL;
...
END CASE userid
FROM myTable ...
It will get a bit cumbersome if there are a lot of values in the array.
http://dev.mysql.com/doc/refman/5.0/en/case.html
You can't do this with SQL alone because the id is not defined in the table (not withstanding the cumbersome case statement proposed by Hulka).
Less directly answer the question but providing an alternate solution:
However, you can make a 'small' modification to your php which will accomplish the desired result:
$user_string = implode("','", array_values($users));
foreach ($users as $k => $v){
$query[$k] = "SELECT \"$v\"
,username
,colFoo
,colBar
FROM myTable WHERE username IN ('$user_string')";
}
the query is is basically the same as 'SELECT "6" as user_id, field from TABLE;' for each user, this which would return
6 field
----------
6 value1
6 value2
...
depending on your interface you may be able to prepare and execute, but you did not give enough details to produce specific code.
You would have to run the query multiple times, but you could push or array_merge. Although, I personally think you should use the user_ids as the indexes for this new array. That would require a modest rewrite of above.

MySQL Query Pegs Server at 100% - Sometimes

I've got a MySQL query that runs very, very slowly and pegs the server usage at 100% as soon as it's executed....sometimes.
Here's the query:
SELECT DISTINCT r1.recordID,
(SELECT MAX(r2.date)
FROM reminders as r2
WHERE r2.owner = '$owner'
AND r2.recordID = r1.recordID
AND r2.status = 'Active'
AND r2.followUp != 'true'
ORDER BY r2.date DESC LIMIT 1) as maxDate
FROM reminders as r1
WHERE r1.owner = '$owner'
AND (SELECT MAX(r2.date)
FROM reminders as r2
WHERE r2.recordID = r1.recordID
AND r2.status = 'Active'
AND r2.followUp != 'true'
ORDER BY r2.date DESC LIMIT 1) <= '$date'
AND (SELECT do_not_call
FROM marketingDatabase
WHERE id = r1.recordID) != 'true'
AND r1.status = 'Active'
ORDER BY maxDate DESC
I'm not sure if it's a poorly written query (might be...I'm new at this) or something else. Sometimes it works fine and results are returned almost instantly but other times it takes a long time (15+ minutes) and 100% server resources to return the results.
Any idea why this could be happening? Anything I can do to the query to prevent this?
Thanks in advance!
[EDIT]
Here's the EXPLAIN.
Array
(
[0] => Array
(
[id] => 1
[select_type] => PRIMARY
[table] => r1
[type] => ALL
[possible_keys] =>
[key] =>
[key_len] =>
[ref] =>
[rows] => 2073
[Extra] => Using where; Using temporary; Using filesort
)
[1] => Array
(
[id] => 4
[select_type] => DEPENDENT SUBQUERY
[table] => marketingDatabase
[type] => eq_ref
[possible_keys] => PRIMARY
[key] => PRIMARY
[key_len] => 4
[ref] => teleforce.r1.recordID
[rows] => 1
[Extra] =>
)
[2] => Array
(
[id] => 3
[select_type] => DEPENDENT SUBQUERY
[table] => r2
[type] => ALL
[possible_keys] =>
[key] =>
[key_len] =>
[ref] =>
[rows] => 2073
[Extra] => Using where
)
[3] => Array
(
[id] => 2
[select_type] => DEPENDENT SUBQUERY
[table] => r2
[type] => ALL
[possible_keys] =>
[key] =>
[key_len] =>
[ref] =>
[rows] => 2073
[Extra] => Using where
)
)
Several things could make this query slow.
One: In general, the first thing to check is indexes. You have three embedded queries that have to be executed for each record in r1. If any of these are not able to effectively use an index and have to process a large number of records, this query will be very slow. Review your indexes, and use "explain" to see what's being used.
Two: Embedded queries tend to be slower than joins. See if you can't transform some of your embedded queries to joins.
Three: In this particular query, you're joining "remainders" back on itself, as far as I can figure out, just to find max(date). Why not just use a GROUP BY? My step 1 to improving this query would be:
select r1.recordID, max(r1.date) as maxdate
from reminders as r1
where r1.owner=$owner and r1.status='Active' and r1.followUp!='true'
and (SELECT do_not_call FROM marketingDatabase WHERE id = r1.recordID) != 'true'
group by r1.recordID
having max(r1.date)<=$date
order by maxdate desc
I don't have your database to test this, but I think it would give the same results.
Four: I'd turn the other embedded query into a join. Like:
select r1.recordID, max(r1.date) as maxdate
from reminders as r1
join marketingDatabase as m on m.id=r1.recordID
where r1.owner=$owner and r1.status='Active' and r1.followUp!='true'
and m.do_not_call != 'true'
group by r1.recordID, r1.owner
having max(r1.date)<=$date
order by maxdate desc
(I'm not sure what a recordID identifies. It appears from your query that you can have multiple records in reminders with the same recordid.)
Five: You'll probably get best performance if you have indexes on reminders(owner, date) and marketingDatabase(id).
Six: Just by the way, if "do_not_call" and followUp are true/false, they should be booleans and not varchars. You're just wasting disk space and execution time processing "true" instead of a boolean TRUE. And you create the problem of mis-spellings, like "ture" or "True". At the absolute worst, make them a char(1), T or F.
There are times when you need embedded queries, but they should be a last resort.
Try to create an index on reminders.owner with the command
CREATE INDEX someNameYouChoose ON reminders(owner);

Categories