I found some similar questions with good answers but i couldnt figure out how to apply this to my specific case. I have a site where users can rate there favorite post from 1-6. Every number is a different category.
Now i need to know the most frequently votes for every single post. So i need to count every post id and than the most frequent values of every post id.
After that i wanna update every result in another table. (dont know how to figure this out right now i'm not that good with Mysql yet).
this are the two columns where i need to know how often every post exist in post_id and what is the most frequently voting number of every single post.
just an example of my table (value = voting)
value | post_id
---------------
3 | 12
1 | 6
4 | 13
2 | 5
6 | 12
5 | 6
i need the output like this to know which post is mostly votet for which category.
post | most voted in this category
---------------
1 | 3
2 | 5
3 | 6
4 | 1
5 | 4
6 | 6
i need this for every post in the table. and than i would need to update every post in another table. i guess i have to do this in a loop.
but im already stuck at the first part.
all i have is this. for the first part.
<?php global $wpdb;
$test = $wpdb->get_results('SELECT posts_id, value, COUNT(posts_id) AS ActionCount
FROM rating_item_entry_value
GROUP BY posts_id
ORDER BY ActionCount DESC');
echo '<pre>';
print_r($test);
echo '</pre>';
and this is the output i get
Array
(
[0] => stdClass Object
(
[posts_id] => 0
[value] => 5
[ActionCount] => 7
)
[1] => stdClass Object
(
[posts_id] => 221
[value] => 3
[ActionCount] => 3
)
[2] => stdClass Object
(
[posts_id] => 197
[value] => 5
[ActionCount] => 2
)
[3] => stdClass Object
(
[posts_id] => 164
[value] => 3
[ActionCount] => 1
)
)
for the example.
I have no idea how to do this better, trying a lot but can't figure it out. does anyone has a good solution how to get the most frequent number for every single id? (and maybe how to safe the results in a variable to update every post in another table within a loop?) thank u so much for any help. regards
most frequently means count aggregation and group by frequency. you can map this to your problem:
select
x.amount,
count(*) as times -- I forgot that row
from
X x
group by
x.amount
order by
count(*) DESC
// edit to you mean that
select
post_id,
value,
count(*)
from
your_table
group by
post_id,
value
order by
count(*) desc
Related
My data in table t1 as below (only 2 record),
+-----------+-----------------+----------+
| shid | lvlmin | lvlmax |
+-----------+-----------------+----------+
| 1 | 1 | 10 |
| 2 | 5 | 10 |
+----------------------------------------+
My php code is:
$userinfo[0] = '9';
$ghunt = DB::fetch_all("SELECT shid FROM t1
WHERE lvlmin <= ".$userinfo[0]." AND lvlmax >= ".$userinfo[0].
"ORDER BY rand() LIMIT 5");
print_r($ghunt);
Result got 2 array:
Array ( [0] => Array ( [shid] => 2 ) [1] => Array ( [shid] => 1 ) )
How do I do when the array result is less than the LIMIT 5 in mysql query, auto use the array result in $ghunt to fill up the array?
What I mean is:
Array (
[0] => Array ( [shid] => 2 )
[1] => Array ( [shid] => 1 )
[2] => Array ( [shid] => 2 )
[3] => Array ( [shid] => 1 )
[4] => Array ( [shid] => 1 )
)
The shid can be random place in array.
Why don't you do something like this?
If (count($ghunt) < 5){
$realResultCount = count($ghunt);
for ($i = realResultCount; $i <= 5; $i++){
$ghunt[$i] = $ghunt[rand(0,realResultCount-1)];
}
}
Basically, what above code does is, if ghunt has less than 5 records in it, it tops it up to 5, by randomly selecting records out of initially returned records.
I don't code PHP, but I can describe one way you can achieve your goal simply. Most languages have a MOD operator, usually % - the php manual page for mod is here
It gives us the remainder of a division operation, so 10 mod 3 is 1, because 10 divided by 3 is 9 remainder 1
A useful property of MOD then, is that it always cycles between 0 and 1 less than what you're modding by. If you mod an incrementing number by 5, the result will always be 0,1,2,3,4,0,1,2,3,4 in a cycle. This means you can have a for loop with some incrementing number, mod by an array length and the result will be an integer that is certainly an array index. If the loop variable goes higher than the end of the array, the mod operator will make it wrap round to the start of the array again
MyArray[ 1746262848 mod MyArray.length ]
Will certainly not crash, even if the array only has 2 items
So for your case, just have a loop.. make he following pseudo code into PHP
// run the loop 5 times
For I as integer = 0 to 4 do
Print MyArray[ i mod MyArray.length ]
If you have 2 items in your array, A and B, it will simply print ABABA
If you have 3 items A B C it will print ABCAB
Hopefully this info will be helpful to you for implementing a solution in php for this, and many future problems. Mod can be really useful for implementing various things when working with arrays
on my ratings table for my software i have 4 fields.
id autoincrement
rvid vendor id
ratedate date of rating
rating the actual numeric rating
I have done alot with it over the last few months but this time im stumped and i cant get a clear picture in my head of the best way to do this. What i am trying to do is find out if the vendor has had 3 low 'consecutive' ratings. If their last three ratings have been < 3 then i want to flag them.
I have been playing with this for a few hours now so i thought i would ask (not for the answer) but for some path direction just to push me forward, im stuck in thought going in circles here.
I have tried GROUP BY and several ORDER BY but those attempts did not go well and so i am wondering if this is not a mysql answer but a php answer. In other words maybe i just need to take what i have so far and just move to the php side of things via usort and the like and do it that way.
Here is what i have so far i did select id as well at first thinking that was the best way to get the last consective but then i had a small breakthrough that if they have had 3 in a row the id does not matter, so i took it out of the query.
$sql = "SELECT `rvid`, `rating` FROM `vendor_ratings_archive` WHERE `rating` <= '3' ORDER BY `rvid` DESC";
which give me this
Array
(
[0] => Array
(
[rvid] => 7
[rating] => 2
)
[1] => Array
(
[rvid] => 5
[rating] => 1
)
[2] => Array
(
[rvid] => 5
[rating] => 0
)
[3] => Array
(
[rvid] => 5
[rating] => 3
)
)
this is just just samples i tossed in the fields, and there are only 4 rows here where as in live it will be tons of rows. But basically this tells me that these are the vendors that have low ratings in the table. And that is where i get stumpted. I can only do one sort in the query so that is why i am thinking that i need to take this and move to the php side to finish it off.
I think i need to sort the elements by rvid with php first i think, and then see if three elements in a row are the same vender (rvid).
Hope that makes sense. My brain hurts lol...
update - here is all of the table data using *
Array
(
[0] => Array
(
[id] => 7
[rvid] => 7
[ratedate] => 2016-05-01
[rating] => 2
)
[1] => Array
(
[id] => 8
[rvid] => 5
[ratedate] => 2016-05-01
[rating] => 1
)
[2] => Array
(
[id] => 6
[rvid] => 5
[ratedate] => 2016-05-01
[rating] => 0
)
[3] => Array
(
[id] => 5
[rvid] => 5
[ratedate] => 2016-05-01
[rating] => 3
)
)
Here's one way you can begin approaching this - completely in SQL:
Get the last rating for the vendor. ORDER BY date DESC, limit 1.
Get the second to last rating for the vendor. ORDER BY date DESC, limit 1, OFFSET 1.
Then write a query that does a LEFT join of the first two tables. You will have a dataset that has three columns:
vendor id
last rating
second to last rating
Then you can write an expression that says "if column1 is <3 and column2 < 3, then this new column is true"
You should be able to extend this to three columns relatively easily.
Here is what a came up with to solve this riddle. I think explaining it on here helped as well as Alex also helped as he keyed my brain on using the date. I first started looking at using if statment inside of the query and actually that got my brain out of the box and then it hit me what to do.
It is not perfect and certainly could use some trimming to reduce the code, but i understand it and it seems to work, so that is par for me on this course.
the query...
$sql = "SELECT `rvid`, `ratedate`,`rating` FROM `vendor_ratings_archive` WHERE `rating` <= '3' ORDER BY `ratedate`, `rvid` DESC";
which gives me this
Array
(
[0] => Array
(
[rvid] => 7
[ratedate] => 2016-05-01
[rating] => 2
)
[1] => Array
(
[rvid] => 5
[ratedate] => 2016-05-01
[rating] => 1
)
[2] => Array
(
[rvid] => 5
[ratedate] => 2016-05-01
[rating] => 0
)
[3] => Array
(
[rvid] => 5
[ratedate] => 2016-05-01
[rating] => 3
)
)
notice how vendor (rvid) 5 is grouped together which is an added plus.
next a simple foreach to load a new array
foreach($results as $yield)
{
$rvidarray[] = $yield['rvid'];
}//close foreach
which gives me this
Array
(
[0] => 7
[1] => 5
[2] => 5
[3] => 5
)
then we count the array values to group dups
$rvidcounter = array_count_values($rvidarray);
which results in this
Array(
[7] => 1
[5] => 3
)
so now vender 7 as 1 low score and vendor 5 has 3 low scores and since they were already sorted by date i know that its consecutive. Well it sounds good anyway lol ")
then we create our final array with another foreach
foreach($rvidcounter as $key => $value)
{
//anything 3 or over is your watchlist
if($value > 2)
{
$watchlist[] = $key; //rvid number stored
}
}//close foreach
which gives me this
Array
(
[0] => 5
)
this was all done in a service function. So the final deal is everyone in this array has over 3 consecutive low ratings and then i just use a returned array back in my normal php process file and grab the name of each vender by id and pass that to the html and print out the list.
done...
please feel free to improve on this if you like. I may or may not use it because the above code makes sense to me. Something more complicated may not make sense to me 6 mos from now lol But it would be interesting to see what someone comes up with to shorten the process a bit.
Thanks so much and Happy Coding !!!!!
Dave :)
You could do it in SQL like that:
SET #rvid = -1;
SELECT DISTINCT rvid FROM
(
SELECT
rvid,
#neg := rating<3, /* 0 or 1 for addition in next line */
#count := IF(#rvid <> rvid , #neg, #count+#neg) AS `count`, /* set or add */
#rvid := rvid /* remember last row */
FROM
testdb.venrate
ORDER BY
rvid, datetime desc
) subq
WHERE count>=3
;
You set a variable to a non existing id. In each chronologically sorted row you check if rating is too low, that results in 1 (too low) or 0 (ok). If rvid is not equal to the last rvid, it means a new vender section is beginning. On begin of section set the value 0 or 1, else add this value. Finally store the current row's rvid for comparison in next row process.
The code above is looking for 3 consecutive low ratings (low means a value less than 3) over all the time.
A small modification checks if all the latest 3 ratings has been equal to or less than 3:
SET #rvid = -1;
SELECT DISTINCT
rvid
FROM
(
SELECT
rvid,
#high_found := rating>3 OR (#rvid = rvid AND #high_found) unflag,
#count := IF(#rvid <> rvid , 1, #count+1) AS `count`,
#rvid := rvid /* remember last row */
FROM
testdb.venrate
ORDER BY
rvid, datetime desc
) subq
WHERE count=3 AND NOT unflag
;
I have a table , similar to this:
| key | value |
|----------|-------|
| limit | 15 |
| viplimit | 25 |
| .. | |
And i have an array :
Array
(
[0] => Array
(
[key] => limit
[value] => 10
)
[1] => Array
(
[key] => viplimit
[value] => 99
)
...
Now , saying we have 100 rows. What would be the best way to update the table corresponding to the array ?
There would be the option of a query for each 100 row, but that is just bad performance.
This should work:
$statement = "UPDATE mytable
SET key = CASE id
WHEN 1 THEN 'key'
WHEN 2 THEN 'another_key'
WHEN 3 THEN 'some_key'
END,
value = CASE id
WHEN 1 THEN 15
WHEN 2 THEN 25
WHEN 3 THEN 45
END
WHERE id IN (1, 2, 3)
");
DB::statement($statement);
Just think how to create correct query. If it's admin panel or something that will be run not very often, I'd just use iteration to keep things simple.
Example of mysql is here http://sqlfiddle.com/#!9/68653/2
Mysql table (named topics)
TopicId | TopicName | ClosestUpperLevelId
--------------------------------------------
1 | Books | 0
2 | BooksAboutCss | 1
3 | BooksAboutHtml | 1
4 | BooksAboutCss1 | 2
5 | BooksAboutCss2 | 2
6 | BooksAboutHtml1 | 3
7 | BooksAboutHtml2 | 3
8 | E-Books | 0
9 | Magazines | 0
For top level topics ClosestUpperLevelId is 0. For subtopics ClosestUpperLevelId is TopicId of closest upper level topic
(TopicId - ClosestUpperLevelId)
Books (1-0)
BooksAboutCss (2-1)
BooksAboutCss1 (4-2)
BooksAboutCss2 (5-2)
BooksAboutHtml (3-1)
BooksAboutHtml1 (6-3)
BooksAboutHtml2 (7-3)
E-Books (8-0)
Magazines (9-0)
For example, i have created one page and location of the page is domain.com/Books/BooksAboutCss/BooksAboutCss2
Now i decided to edit the page. For example i want to edit location (topic or category) of the page and set it to domain.com/Magazines. So i need to fetch all topics, related with existing (saved) page. Then will create select boxes for each group (level) of topics.
At the moment tried to use php while and multiple times to connect to mysql and get data. Like below code. How can i get the same result without php while? How connect to mysql only once and fetch all necessary data? Do i need to use mysql while https://dev.mysql.com/doc/refman/5.1/en/while.html?
$topic_names_1[0]['UpperLevelNumberRenamed'] = 5;//just set some value to start to fetch
while ( trim($topic_names_1[0]['ClosestUpperLevelId']) != 0 ){
try {
$stmt_1 = $db->prepare('
SELECT `TopicId`, `TopicName`, `ClosestUpperLevelId`
FROM `topics`
WHERE `ClosestUpperLevelId` =
(
SELECT `ClosestUpperLevelId`
FROM `topics`
WHERE `TopicId` = ?
)
;');
$stmt_1->execute( array( trim($topic_names_1[0]['UpperLevelNumberRenamed']) ) );
$topic_names_1 = $stmt_1->fetchAll(PDO::FETCH_ASSOC);
echo '<pre>', print_r($topic_names_1), ' topic_names_1 __</pre>';
}//try {
catch (PDOException $e){
echo "<br> stmt_1 DataBase Error: " .htmlspecialchars( $e->getMessage() , ENT_QUOTES, "UTF-8").'<br>';
}
catch (Exception $e) {
echo " stmt_1 General Error: ".htmlspecialchars( $e->getMessage() ).'<br>';
}
}//while ( trim($topic_names[0]['UpperLevelNumberRenamed']) != 0 )
As result get arrays like this
Array
(
[0] => Array
(
[TopicId] => 4
[TopicName] => BooksAboutCss1
[ClosestUpperLevelId] => 2
)
[1] => Array
(
[TopicId] => 5
[TopicName] => BooksAboutCss2
[ClosestUpperLevelId] => 2
)
)
1 topic_names_1 __
Array
(
[0] => Array
(
[TopicId] => 2
[TopicName] => BooksAboutCss
[ClosestUpperLevelId] => 1
)
[1] => Array
(
[TopicId] => 3
[TopicName] => BooksAboutHtml
[ClosestUpperLevelId] => 1
)
)
1 topic_names_1 __
Array
(
[0] => Array
(
[TopicId] => 1
[TopicName] => Books
[ClosestUpperLevelId] => 0
)
[1] => Array
(
[TopicId] => 8
[TopicName] => E-Books
[ClosestUpperLevelId] => 0
)
[2] => Array
(
[TopicId] => 9
[TopicName] => Magazines
[ClosestUpperLevelId] => 0
)
)
1 topic_names_1 __
Update
Found one example with mysql while. Trying to create own code
Created this
SET `ClosestUpperLevelId` := 2;
WHILE `ClosestUpperLevelId` > 0
DO
SELECT `TopicId`, `TopicName`, `ClosestUpperLevelId`;
END WHILE;
and this (http://sqlfiddle.com/#!9/68653/7)
CREATE PROCEDURE dowhile()
BEGIN
DECLARE `ClosestUpperLevelId` INT DEFAULT 2;
WHILE `ClosestUpperLevelId` > 0 DO
SELECT `TopicId`, `TopicName`, `ClosestUpperLevelId`;
END WHILE;
END;
Got error...
You could just move your select statement before the while loop and take out the topic_id from the where clause. Then in your loop retrieve from the complete record set using an array search function or similar. By running same query once your dB will be able to cache results
Relational databases like MySQL aren't naturally good at storing hierarchical data, but there are ways of doing it. The two ways I know of are "adjacency lists" and "nested sets".
For an adjacency list, you'd simply store a "parent_id" field for each row. Root rows (that have no parent) can just have NULL for their parent_id field. Adjacency lists are easy to manage, but not very flexible and require recursive queries to find a path from root to leaf (unless you're only going 2 levels deep, then you can simply JOIN).
Here's an example:
id | parent_id | name
0 NULL grandfather
1 0 father
2 1 grandson
3 1 granddaughter
This query would help assemble the data:
SELECT * FROM
`people` AS p1
JOIN `people` p2
ON p1.id = p2.parent_id
Any more than those two levels and you need recursion. Alternatively, you could just query the entire table and assemble it in code.
Nested sets are a little more complicated, but allow you to easily query all the way up the tree for a given leaf node. It's much easier to understand nested sets at first by seeing a visual, check this out:
https://en.wikipedia.org/wiki/Nested_set_model
And here's what your schema would look like:
left | right | name
0 7 grandfather
1 6 father
2 3 grandson
4 5 granddaughter
And here's an example to fetch father and children:
SELECT *
FROM `people`
WHERE `left` >= 1 AND `right` <= 6
Nested sets have the downside that the entire table's left and right values need to be updated when the hierarchy changes.
Google "managing hierarchical data in mysql" for more information. I hope this helps.
Let's say I have two tables, people and families.
families has two fields - id and name. The name field contains the family surname.
people has three fields - id, family_id and name - The family_id is the id of the family that that person belongs to. The name field is that person's first name.
It's basically a one to many relationship with one family having many people.
I want to get a lists of name sets, ordered by the highest occurrence of the largest set of names across families.
That probably doesn't make much sense...
To explain what I want further, we can score each set of names. The 'score' is the array size * number of occurrences across families.
For example, let's say two names, 'John' and 'Jane' both existed in three families - That set's 'score' would be 2*3 = 6.
How could I get an array of sets of names, and the set's 'score', ordered by each set's score?
Sample Result Set (I've put it in a table layout, but this could be a multi-dimensional array in PHP) - Note this is just randomly thought up and doesn't reflect any statistical name data.
names | occurrences | score
Ben, Lucy | 4 | 8
Jane, John | 3 | 6
James, Rosie, Jack | 2 | 6
Charlie, Jane | 2 | 4
Just to clarify, I'm not interested in sets where:
The number of occurrences is 1 (obviously, just one family).
The set size is 1 (just a common name).
I hope I have explained my somewhat complex problem - if anyone needs clarification please say.
OK, got it:
<?php
require_once('query.lib.php');
$db=new database(DB_TYPE,DB_HOST,DB_USER,DB_PASS,DB_MISC);
$qry=new query('set names utf8',$db);
//Base query, this filters out names that are in just one family
$sql='select name, cast(group_concat(family order by family) as char) as famlist, count(*) as num from people group by name having num>0 order by num desc';
$qry=new query($sql,$db);
//$qry->result is something like
/*
Array
(
[name] => Array
(
[0] => cathy
[1] => george
[2] => jack
[3] => john
[4] => jane
[5] => winston
[6] => peter
)
[famlist] => Array
(
[0] => 2,4,5,6,8
[1] => 2,3,4,5,8
[2] => 1,3,5,7,8
[3] => 1,2,3,6,7
[4] => 2,4,7,8
[5] => 1,2,6,8
[6] => 1,3,6
)
[num] => Array
(
[0] => 5
[1] => 5
[2] => 5
[3] => 5
[4] => 4
[5] => 4
[6] => 3
)
)
$qry->rows=7
*/
//Initialize
$names=$qry->result['name'];
$rows=$qry->rows;
$lists=array();
for ($i=0;$i<$rows;$i++) $lists[$i]=explode(',',$qry->result['famlist'][$i]);
//Walk the list and populate pairs - this filters out pairs, that are specific to only one family
$tuples=array();
for ($i=0;$i<$rows;$i++) {
for ($j=$i+1;$j<$rows;$j++) {
$isec=array_intersect($lists[$i],$lists[$j]);
if (sizeof($isec)>1) {
//Every tuple consists of the name-list, the family list, the length and the latest used name
$tuples[]=array($names[$i].'/'.$names[$j],$isec,2,$j);
}
}
}
//Now walk the tuples again rolling forward, until there is nothing left to do
//We do not use a for loop just for style
$i=0;
while ($i<sizeof($tuples)) {
$tuple=$tuples[$i];
//Try to combine this tuple with all later names
for ($j=$tuple[3]+1;$j<$rows;$j++) {
$isec=array_intersect($tuple[1],$lists[$j]);
if (sizeof($isec)>0) $tuples[]=array($tuple[0].'/'.$names[$j],$isec,$tuple[2]+1,$j);
}
$i++;
}
//We have all the tuples, now we just need to extract the info and prepare to sort - some dirty trick here!
$final=array();
while (sizeof($tuples)>0) {
$tuple=array_pop($tuples);
//name list is in $tuple[0]
$list=$tuple[0];
//count is sizeof($tuple[1])
$count=sizeof($tuple[1]);
//length is in $tuple[2]
$final[]=$tuple[2]*$count."\t$count\t$list";
}
//Sorting and output is all that is left
rsort($final);
print_r($final);
?>
I am sorry I just realized I use a query lib that I can't source in here, but from the comment you will easily be able to create the arrays as in the section "Initialize".
Basically what I do is starting with the pairs I keep an array of the families all the names in the current name list belong to, then intersect it with all not-yet tried names.
Will this work?
SELECT
f.name AS 'surname',
GROUP_CONCAT(DISTINCT p.name ORDER BY p.name) AS 'names',
COUNT(DISTINCT p.name) AS 'distinct_names',
COUNT(p.id) AS 'occurrences',
COUNT(DISTINCT p.name) * COUNT(p.id) AS 'score'
FROM
families f
LEFT JOIN people p ON ( f.id = p.family_id )
GROUP BY
f.id
ORDER BY
f.name