group by months and then group results by user id - php

I need to count employ's salary which depends on the number of rows added per months (with a unique picture_id, so if the same picture ID appears twice it counts only one time) - so as I understand I need to group by both picture_id and by months.
It works perfectly fine if I do it separately i.e.
group by MONTH(TimeAdded)
or
GROUP BY picture_id
Here is the code
$data_qqq = mysql_query("SELECT MONTHNAME(TimeAdded), COUNT(MONTH(TimeAdded)) FROM Picture_Tag WHERE UserID = $user_id GROUP BY MONTH(TimeAdded)");
but this counts number of entries added each months even if there are 20 rows with the same picture_id.
Now if I try
$data_qqq = mysql_query("SELECT MONTHNAME(TimeAdded), COUNT(MONTH(TimeAdded)) FROM Picture_Tag WHERE UserID = $user_id GROUP BY MONTH(TimeAdded),**picture_id**");
I get a very strange messed up results...
Any idea how to count number of rows added each months with the unique picture_id?

Without samples it can be difficult to visualize the data, but if you wish to count the number of unique picture_id's each month then use the COUNT(DISTINCT picture_id). But also note when grouping by "month" you most probably also need to group by year otherwise the result could be badly skewed. (Alternatively use a where clause to only the select the single year you are interested in.)
Try this:
SELECT
YEAR(TimeAdded)
, MONTH(TimeAdded)
, COUNT(DISTINCT picture_id)
FROM Picture_Tag
WHERE UserID = $USER_ID
GROUP BY
YEAR(TimeAdded)
, MONTH(TimeAdded)

Related

How do I improve the speed of these PHP MySQLi queries without indexing?

Lets start by saying that I cant use INDEXING as I need the INSERT, DELETE and UPDATE for this table to be super fast, which they are.
I have a page that displays a summary of order units collected in a database table. To populate the table an order number is created and then individual units associated with that order are scanned into the table to recored which units are associated with each order.
For the purposes of this example the table has the following columns.
id, UID, order, originator, receiver, datetime
The individual unit quantities can be in the 1000's per order and the entire table is growing to hundreds of thousands of units.
The summary page displays the number of units per order and the first and last unit number for each order. I limit the number of orders to be displayed to the last 30 order numbers.
For example:
Order 10 has 200 units. first UID 1510 last UID 1756
Order 11 has 300 units. first UID 1922 last UID 2831
..........
..........
Currently the response time for the query is about 3 seconds as the code performs the following:
Look up the last 30 orders by by id and sort by order number
While looking at each order number in the array
-- Count the number of database rows that have that order number
-- Select the first UID from all the rows as first
-- Select the last UID from all the rows as last
Display the result
I've determined the majority of the time is taken by the Count of the number of units in each order ~1.8 seconds and then determining the first and last numbers in each order ~1 second.
I am really interested in if there is a way to speed up these queries without INDEXING. Here is the code with the queries.
First request selects the last 30 orders processed selected by id and grouped by order number. This gives the last 30 unique order numbers.
$result = mysqli_query($con, "SELECT order, ANY_VALUE(receiver) AS receiver, ANY_VALUE(originator) AS originator, ANY_VALUE(id) AS id
FROM scandb
GROUP BY order
ORDER BY id
DESC LIMIT 30");
While fetching the last 30 order numbers count the number of units and the first and last UID for each order.
while($row=mysqli_fetch_array($result)){
$count = mysqli_fetch_array(mysqli_query($con, "SELECT order, COUNT(*) as count FROM scandb WHERE order ='".$row['order']."' "));
$firstLast = mysqli_fetch_array(mysqli_query($con, "SELECT (SELECT UID FROM scandb WHERE orderNumber ='".$row['order']."' ORDER BY UID LIMIT 1) as 'first', (SELECT UID FROM barcode WHERE order ='".$row['order']."' ORDER BY UID DESC LIMIT 1) as 'last'"));
echo "<td align= center>".$count['count']."</td>";
echo "<td align= center>".$firstLast['first']."</td>";
echo "<td align= center>".$firstLast['last']."</td>";
}
With 100K lines in the database this whole query is taking about 3 seconds. The majority of the time is in the $count and $firstlast queries. I'd like to know if there is a more efficient way to get this same data in a faster time without Indexing the table. Any special tricks that anyone has would be greatly appreciated.
Design your database with caution
This first tip may seems obvious, but the fact is that most database problems come from badly-designed table structure.
For example, I have seen people storing information such as client info and payment info in the same database column. For both the database system and developers who will have to work on it, this is not a good thing.
When creating a database, always put information on various tables, use clear naming standards and make use of primary keys.
Know what you should optimize
If you want to optimize a specific query, it is extremely useful to be able to get an in-depth look at the result of a query. Using the EXPLAIN statement, you will get lots of useful info on the result produced by a specific query, as shown in the example below:
EXPLAIN SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column;
Don’t select what you don’t need
A very common way to get the desired data is to use the * symbol, which will get all fields from the desired table:
SELECT * FROM wp_posts;
Instead, you should definitely select only the desired fields as shown in the example below. On a very small site with, let’s say, one visitor per minute, that wouldn’t make a difference. But on a site such as Cats Who Code, it saves a lot of work for the database.
SELECT title, excerpt, author FROM wp_posts;
Avoid queries in loops
When using SQL along with a programming language such as PHP, it can be tempting to use SQL queries inside a loop. But doing so is like hammering your database with queries.
This example illustrates the whole “queries in loops” problem:
foreach ($display_order as $id => $ordinal) {
$sql = "UPDATE categories SET display_order = $ordinal WHERE id = $id";
mysql_query($sql);
}
Here is what you should do instead:
UPDATE categories
SET display_order = CASE id
WHEN 1 THEN 3
WHEN 2 THEN 4
WHEN 3 THEN 5
END
WHERE id IN (1,2,3)
Use join instead of subqueries
As a programmer, subqueries are something that you can be tempted to use and abuse. Subqueries, as show below, can be very useful:
SELECT a.id,
(SELECT MAX(created)
FROM posts
WHERE author_id = a.id)
AS latest_post FROM authors a
Although subqueries are useful, they often can be replaced by a join, which is definitely faster to execute.
SELECT a.id, MAX(p.created) AS latest_post
FROM authors a
INNER JOIN posts p
ON (a.id = p.author_id)
GROUP BY a.id
Source: http://20bits.com/articles/10-tips-for-optimizing-mysql-queries-that-dont-suck/

Get previous 10 row from specific WHERE condition

Im currently working on a project that requires MySql database and im having a hard time constructing the query that i want get.
i want to get the previous 10 rows from the specific WHERE condition on my mysql query.
for example
My where is date='December';
i want the last 10 months to as a result.
Feb,march,april,may,june,july,aug,sept,oct,nov like that.
Another example is.
if i have a 17 strings stored in my database. and in my where clause i specify that WHERE strings='eyt' limit 3
Test
one
twi
thre
for
payb
six
seven
eyt
nayn
ten
eleven
twelve
tertin
fortin
fiftin
sixtin
the result must be
payb
six
seven
Thanks in advance for your suggestions or answers
If you are using PDO this is the right syntax:
$objStmt = $objDatabase->prepare('SELECT * FROM calendar ORDER BY id DESC LIMIT 10');
You can change ASC to DESC in order to get either the first or the last 10.
Here's a solution:
select t.*
from mytable t
inner join (select id from mytable where strings = 'eyt' order by id limit 1) x
on t.id < x.id
order by t.id desc
limit 3
Demo: http://sqlfiddle.com/#!9/7ffc4/2
It outputs the rows in descending order, but you can either live with that, or else put that query in a subquery and reverse the order.
Re your comment:
x in the above query is called a "correlation name" so we can refer to columns of the subquery as if they were columns of a table. It's required when you use a subquery as a table.
I chose the letter x arbitrarily. You can use anything you like as a correlation name, following the same rules you would use for any identifier.
You can also optionally define a correlation name for any simple table in the query (like mytable t above), so you can refer to columns of that table using a convenient abbreviated name. For example in t.id < x.id
Some people use the term "table alias" but the technical term is "correlation name".

mysql php COUNT by same value in column GROUP BY to echo out

I'm trying to figure out a solution to a query.
Question :
I need to find the sum of rows which have the same value in a certain column. From this I then need to echo out certain bits of information along with the total count each value is seen (i.e. if 'project_id' has two rows with the same value as 11122 then the total count will be 2). I don't know the values I will need as these will be random project numbers in my table.
The column name I need to count by is 'project_id'.
I can't figure out how to echo in the following way:
Project ID = 11122 Total Reviews = 2
Project ID = 99999 Total Reviews = 5
Thanks!
As mentioned above in the comments, Sean's answer works a treat!
SELECT project_id, COUNT(*) as TotalReviews FROM YourTable GROUP BY project_id

Join SQL query that has single id/row in 1 table, but links to multiple rows(same id) in another table?

First sorry for the long question title.
My question/situation is as such.
1.) I have 2 tables in mysql
2.) In first table, each listing has a unique id(each listing is in 1 row)
3.) In the second table it has the name/tags for images linked to the listing id,from the first table
4.) Each listing can have multiple images(multiple row in the second table).
What i am trying to do is to pull all the listings from table 1 and then use the listing.id from table one to pull all the rows of images from table 2 that are linked to the listing.id.
I am confused at the moment because there are multiple rows that has the same listing.id from table 2. ANd i tried query to display* but it only echo the last image(row) from table 2.
It doesnt seem to work when i join the 2 tables. And i am not sure if i query it twice then push array together.
Thanks for your time
$result=mysqli_query($con,"SELECT * FROM Listing JOIN listingpic ON
(Listing.id = listingpic.listingid)
WHERE date >= curdate() - INTERVAL DAYOFWEEK(curdate())+300 DAY GROUP BY Listing.id ORDER BY Listing.id DESC") or die( mysqli_error($con));
while ($row = mysqli_fetch_assoc($result))
{
$output[] = $row;
}
if (!empty($output)){
echo json_encode( $output );}
else{
echo json_encode( [] );
}
You only get one result per id, because you used GROUP BY Listing.id. If you do not group them, you get one result row for each table 2 row, including the Listing id each time.
If you wish to retrieve the data in one query in the form "one id: multiple data", you can use GROUP_CONCAT for example and then explode() the retrieved string result.
Otherwise get all ids from table 1 and then iterate over them in PHP and do one additional query per ID
Pro tip: Don't use the viciously confusing MySQL extension to GROUP BY. Read this: http://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html
Pro tip: Don't use SELECT *, especially when you're joining tables. Instead, enumerate the columns you want in your result set.
Inherent to SQL is the idea that resultsets, like tables, are rectangular. They have rows and columns. Each row usually represents some real world item -- an "entity" -- and each column represents some attribute of that entity.
The result set you describe will, inherently, repeat information from your first table so it can show the info from the second table row by row.
What you want for a query is this, I think.
SELECT Listing.id, listing.date,
listingpic.id, listingpic.url, listingpic.caption
FROM Listing
JOIN listingpic ON Listing.id = listingpic.listingid
WHERE date >= curdate() - INTERVAL DAYOFWEEK(curdate())+300 DAY
ORDER BY Listing.id DESC, listingpic.id
This will give you one row per image.
If you're running out of memory it's because your result set is massive. You may want to limit it somehow, either using a first and last publication date:
WHERE date >= curdate() - INTERVAL DAYOFWEEK(curdate())+300 DAY
AND date < curdate() - INTERVAL DAYOFWEEK(curdate())+293 DAY
or with a LIMIT clause.
ORDER BY Listing.id DESC, listingpic.id
LIMIT 100

Pull unique values from mysql table and order by number of occurrences

I'm having a little trouble with this, I found how to get unique values from multiple columns, and I know how to sort normally, but I can't seem to mix the two
The original sql statement is here:
$sql="SELECT DISTINCT UserID, UserIP FROM Visits WHERE PageID='".$_GET['p']."'";
It only gets the first instance of where both values are unique, so doing the 'mysql_numrows' thing doesn't work.
This is from within the fetch array loop, it can count the occurrences fine, but at that point it's a bit too late to sort
$sql5="SELECT Date, VisitID FROM Visits WHERE PageID='".$_GET['p']."' AND UserID='".$userid."' AND UserIP='".$userip."' ORDER BY Date";
$result5 = mysql_query($sql5);
$num5 = mysql_numrows($result5);
Basically, it should get unique values based on both columns, count how many times the appear, sort by count, then potentially also sort by the highest matching date if possible
Any suggestions would be great, cheers :)
Also it's just for a personal site, I'm not actually doing anything with the info, just trying to learn some more php
Try using GROUP BY, like this:
SELECT UserID, UserIP FROM Visits
WHERE PageID = ###
GROUP BY UserID, UserIP
ORDER BY COUNT(*) DESC, max(`Date`) DESC
Please change your query like
$sql = "SELECT UserID, UserIP FROM Visits
WHERE PageID = '".$_GET['p']."'
GROUP BY UserID, UserIP
ORDER BY COUNT(UserID) DESC";
let me know if helpful to you.
Thanks

Categories