i am having a table named Reports with
id report_id user_id
1 1 5
2 1 5
3 1 5
4 2 5
5 2 5
6 3 6
7 3 6
8 4 1
9 4 1
10 4 1
i am trying to write a Query such that user_id = 5 and to find how many reports he has created.(Answer should be of 2 )
i have a Wrote a Mysql Query as
select count(distinct report_id) from Reports where user_id=5
i m trying the same MYSQl sub Query inside the Foreach users loop where my 5 is from $user['User']['id'];
how to write the MYSQL Query above inside this for loop in cakephp Framework....
foreach($users as & $user):
echo "User id ".$user['User']['id'];
$user['User']['report_count'] = $this->Report->find('count',
array('conditions'=>array('Report.user_id'=>$user['User']['id'])));
endforeach;
$this->set('users', $users);
Please suggest me.......HOw to write the above Mysql Query in cakephp
You want to use the following functions GROUP BY and COUNT
Your query could look somewhat like this
select count(distinct report_id) from Reports where user_id=5
If this is a list of users you are showing in your application... you could significantly reduce the number of queries you are running.
eg. for 100 users you will be running 100 queries instead you can run a single single query to extract the user_id and count of reports by each user
select count(distinct report_id) as count,user_id from Reports where user_id IN (1,2) GROUP BY user_id;
OR if you want to run seperate queries for each user
select count(distinct report_id) as count,user_id from Report where user_id=5;
Try this:
$user['User']['report_count'] = $this->Report->find('count',
array( 'conditions' => array('Report.user_id' => $user['User']['id']),
'fields' => 'DISTINCT Report.report_id'
)
);
It should fetch all distinct report_ids for a given user_id, then count them. Basically, it should run the query:
SELECT DISTINCT report_id FROM Reports WHERE user_id=$user['User']['id']
(after substituting the value of $user['User']['id']), then count the number of rows in the result. Caveat: I don't use CakePHP in real life, I just read the documentation; your mileage may vary. As halocursed mentions, running a single SQL query on your own would be more efficient than calling find(...) for each user ID. You could also try:
$report_counts = $this->Report->find('list',
array( 'conditions' => array('Report.user_id' => array_map(create_function('$user', 'return $user["User"]["id"];'), $users)),
'group' => array('Report.user_id'),
'fields' => array('Report.user_id', 'COUNT(DISTINCT Report.report_id) AS report_count')
)
);
foreach ($users as &$user) {
$user['User']['report_count'] = $report_counts[$user['User']['id']];
}
However, I don't know if CakePHP will accept aggregate functions in the 'fields' parameter, and I don't know as though find('list', ...) will pick Report.user_id as the array index. If you're having problems with the latter, you could switch to a [find('all', ...)][3] call and loop over $report_counts rather than $users. I didn't take this approach because I don't know the structure of $users, such as how it's indexed.
Related
I am working on a project where I have to code for job filters.
In this when I filters for salaries it sends salary id's in array like:
job_salary = array(
[0]=>3,
[1]=>4,
[2]=>5,
[3]=>6,
[4]=>7,
[5]=>8
)
and job in my jobs table contains multiple salaries using comma string.
id salary_ids
1 2,4,6
2 1,3,5
3 4,5
4 9,1
So if i search for value 3, and 5 in array form like
job_salary = array(
[0]=>3,
[1]=>5
);
It should return me the second and third row because these rows contain value 3 and 5.
Use it like below let me know if it works
$values=array("1","2","3");
foreach($values as $val)
{
$query="Select * from table_name where FIND_IN_SET('".$val."',column_name)";
$result=mysql_query($query);
$data=mysql_fetch_array($result);
$dbval[]=$data['column_name'];
}
print_r($dbval);
please try again like this
1).For Postgres MySQL :
SELECT FIND_IN_SET(5,userid) as result FROM department
2).For Postgres SQL :
select *
from TABLENAME f
where 'searchvalue' = ANY (string_to_array(COLOUMNNAME,','))
Example
select *
from customer f
where '11' = ANY (string_to_array(customerids,','))
Help me understand how the sum function works, when I want to count from a relational table field.
The query is pretty simple. I have unitsOrdered table and unitsSent table. The relation is 1-n. So i want to count all sent units of the specific order. In this example order id=2;
In my db I have data.
unitsOrdered:
id
1
2
unitsSent:
id | order_id | units
1 | 1 | 5
2 | 2 | 2
My query is:
query = OrderedUnits::find()
->select([
'ou.*',
'sum(us.units) as alreadySent'
])
->joinWith('unitsSent us')
->where(['ou.id' => 2])
->orderBy('ou.id desc')
->groupBy(['ou.id'])
->all();
The result should be pretty clear:
id 2 = 2 units
However, my query returns:
id 2 = 10 units.
As I understand what i does is 5 * 2 = 10;
What is wrong with my query?
The relation is defined like this:
/**
* #return \yii\db\ActiveQuery
*/
public function getUnitsSent()
{
return $this->hasMany(UnitsSent::className(), ['order_id' => 'id'])
->from(['us' => UnitsSent::tableName()]);
}
Actual query:
'SELECT `ou`.*, sum(us.sent_units) as alreadySent FROM `ordered_units` `ou`
LEFT JOIN `units_sent` `us` ON `ou`.`id` = `us`.`order_id`
WHERE (`ou`.`id`= 2)
GROUP BY `ou`.`id`
ORDER BY `ou`.`id` DESC'
You should use the hash filter format this way ->where(['us.id' => 2])
and for get result 2 you need innerJoin (your raw sql show you have a left join) otherwise also the rows that not match are joined
query = OrderedUnits::find()
->select([
'ou.*',
'sum(us.units) as alreadySent'
])
->innerJoinWith('unitsSent us')
->where(['us.id' => 2])
->orderBy('ou.id desc')
->groupBy(['ou.id'])
->all();
and try check the real query executed using
echo $query->createCommand()->getRawSql();
and remeber that the selection of columns not aggregated and not mentioned in group by is not allowed in mysql starting for 5.7 ( and anyway is not a correct way to use group by in SQL)
I have the following 2 tables
table_article:
id subject tags
---------------------
1 subject-1 2,4,5
2 subject-2 3,5
3 subject-3 1,2
4 subject-4 2,3,4
5 subject-5 3
table_tags:
id tag_name
---------------------
1 php
2 jQuery
3 css
4 mysql
5 java
and I'm trying to get results like
id => 1, subject => subject-1, tag_names => jQuery,mysql,java
id => 2, subject => subject-2, tag_names => css,java
id => 3, subject => subject-3, tag_names => php,jQuery
Here is my current attempt, which returns ONLY the first tag (e.g. 2 instead of 2,4,5 for row 1)
1 SELECT
2 table_article.id,
3 table_article.subject,
4 GROUP_CONCAT(table_tags.tag_name) AS tag_names
5 FROM
6 table_article
7 LEFT JOIN
8 table_tags
9 ON
10 (table_tags.tag_id IN (table_article.tags))
11 GROUP BY
12 table_article.id
13 LIMIT
14 3
and the results are
id => 1, subject => subject-1, tag_names => jquery
id => 2, subject => subject-2, tag_names => css
id => 3, subject => subject-3, tag_names => php
The problem occurs on line 10 -> IN (table_article.tags).
I just can't figure out how could I solve this problem, can anyone help please?
You can't use a string that happens to contain commas as a list of discrete values.
In other words this:
ON table_tags.tag_id IN (2,4,5)
Is not the same as this:
ON table_tags.tag_id IN ('2,4,5')
The numeric value of a string like '2,4,5' is the initial numeric portion, and the remainder after the first non-numeric character is ignored. So the string '2,4,5' has a numeric value of 2. It won't be an error, but it won't get you what you intended, which is a match against any of the values in the comma-separated list.
MySQL has a built-in function FIND_IN_SET() which does understand strings that contain comma-separated values. The function returns the position of the matching value, or 0 if no match was found.
ON FIND_IN_SET(table_tags.tag_id, '2,4,5') > 0
But this cannot use an index and it forces you to run a table-scan which is going to kill your performance. To be clear, I don't recommend using this function in a join condition.
The answer is: Don't store tags in a comma-separated list. See my answer for Is storing a comma separated list in a database column really that bad?
Store one tag per row in a separate table, as #Martin Lyne suggests. That way you can look for the right tag with = and you can even index the column for much better performance.
I've not seen an IN in a ON before (not saying it's not valid) but I would do ON table_tags.tag_id = table_article.tags)
So you end up with multiple rows
subject-1, query
subject-1, css
subject 2, query
then the GROUP BY would compress the table and the GROUP_CONCAT gets all the missing tags.
Well, I would use IN in this situation, it won't work, replace it with FIND_IN_SET(table_tags.tag_id, table_article.tags) > 0 and you'll be fine. Though you really should normalize this.
As other said, this is not a good design.
Instead, you could change your table design this way:
table_article
id
subject
article_tag
article_id
tag_id
table_tags
id
tag_name
Life would be much easier this way :-)
Here is a structure of my tables:
Here is what I want as a result of a query (considering that user provides me with 2|4|1 pattern):
Here is what I tried:
SELECT parcel.TrackCode, parcelType.Name, GROUP_CONCAT(track.DateTime SEPARATOR '|') AS dt
FROM track
JOIN parcel ON track.ParcelID = parcel.ID
JOIN parcelType ON parcel.ParcelTypeID = parcelType.ID
JOIN event ON track.EventID = event.ID
GROUP BY parcel.ID;
The result is:
So the problem is that I need GROUP_CONCAT() to divide data to several fields (date where track.eventID = 3, date where track.eventID = 1, date where track.eventID = 5, date where track.eventID = 7 # considering that the pattern is 3|1|5|7). Any ideas?
I would suggest running two queries. First this one, to get the relevant events:
SELECT ParcelID, EventID, DateTime
FROM track
WHERE EventID IN(1, 2, 4)
Store the results of this query in a map of parcel ids to arrays of events where the key is the parcel ID and the value is another array. In that inner array, the key is the event ID and the value is the event date.
array(1 => array(
2 => '2012-05-15 15:33:00',
4 => '2012-05-22 11:35:41',
1 => '2012-05-04 18:58:30'
),
2 => array(
2 => '2012-07-01 09:05:56',
4 => '2012-07-14 13:32:00',
1 => '2012-06-27 12:44:32'
)
);
Then, use a query like this next one to get the list of parcels, and for each one, you can easily look in memory at the results of the previous query to find out the dates of each of those events, for any given parcel ID.
SELECT parcel.ID, parcel.TrackCode, parceltype.Name
FROM parcel
JOIN parceltype ON parceltype.ID = parcel.ParcelTypeID
Note: this answer is a trimmed version of the conversation that took place in the MySQL chat room
If you want the IDs also, you should GROUP_CONCAT the ids from track, they will be given in the same order too.
Or set an ORDER BY at the end of the query and it will manipulate that data as well.
I have a table like this:
way stop time
1 1 00:55
1 2 01:01
1 3 01:07
2 2 01:41
2 3 01:47
2 5 01:49
3 1 04:00
3 2 04:06
3 3 04:12
and I want a table like this:
stop way_1 way_2 way_3 (way_n)
1 00:55 04:00
2 01:01 01:41 04:06
3 01:07 01:47 04:12
5 01:49
There are many solutions online about MySQL cross tab (pivot table), but how can I do this if I don't know how many "way" are there?
Thanks
The number and names of columns must be fixed at the time you prepare the query. That's just the way SQL works.
So you have two choices of how to solve this. Both choices involve writing application code:
(1) Query the distinct values of way and then write code to use these to construct the pivot query, appending as many columns in the SELECT-list as the number of distinct values.
foreach ($pdo->query("SELECT DISTINCT `way` FROM `MyTable`") as $row) {
$way = (int) $row["way"];
$way_array[] = "MAX(IF(`way`=$way, `time`)) AS way_$way";
}
$pivotsql = "SELECT stop, " . join(", ", $way_array) .
"FROM `MyTable` GROUP BY `stop`";
Now you can run the new query, and it has as many columns as the number of distinct way values.
$pivotstmt = $pdo->query($pivotsql);
(2) Query the data row by row as it is structured in your database, and then write code to pivot into columns before you display the data.
$stoparray = array();
foreach ($pdo->query("SELECT * FROM `MyTable`") as $row) {
$stopkey = $row["stop"];
if (!array_key_exists($stopkey, $stoparray)) {
$stoparray[$stopkey] = array("stop"=>$stopkey);
}
$waykey = "way_" . $row["way"];
$stoparray[$stopkey][$waykey] = $row["time"];
}
Now you have an array of arrays that looks the same as if you had run a pivot query, but the actual SQL you ran was a lot simpler. You post-processed the query result into a different set of arrays.