I am trying to develop a rating system with php/mysql.
I have a simple rating object like this:
(t is type of rating, r is value of rating)
[{"t":"1","r":2},{"t":"2","r":4},{"t":"3","r":1},{"t":"4","r":2},{"t":"5","r":2}]
In DB, I have a lot of rating records
Like this:
object1=> [{"t":"1","r":2},{"t":"2","r":4},{"t":"3","r":1},{"t":"4","r":2},{"t":"5","r":2}]
object2=> [{"t":"1","r":1},{"t":"2","r":5},{"t":"3","r":3},{"t":"4","r":3},{"t":"5","r":1}]
In short for output I need a new object like this (I need to calculate average rating, with same keys.)
objectAverageCalculated=> [{"t":"1","r":1.5},{"t":"2","r":4.5},{"t":"3","r":2},{"t":"4","r":2.5},{"t":"5","r":1.5}]
My sql:
CREATE TABLE `ratings` (
`id` int(11) NOT NULL,
`rating` text NOT NULL,
`item_id` varchar(16) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `ratings` (`id`, `rating`, `item_id`) VALUES
(6, '[{\"t\":\"1\",\"r\":2},{\"t\":\"2\",\"r\":4},{\"t\":\"3\",\"r\":1},{\"t\":\"4\",\"r\":2},{\"t\":\"5\",\"r\":2}]', 'ABC123'),
(7, '[{\"t\":\"1\",\"r\":1},{\"t\":\"2\",\"r\":5},{\"t\":\"3\",\"r\":3},{\"t\":\"4\",\"r\":3},{\"t\":\"5\",\"r\":1}]', 'ABC123');
--
ALTER TABLE `ratings`
ADD PRIMARY KEY (`id`);
ALTER TABLE `ratings`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8;
COMMIT;
My code
$result = mysqli_query($con, "SELECT * FROM ratings WHERE item_id='ABC123' ");
while ($row = mysqli_fetch_array($result)) {
$tempArray = json_decode($row['rating'], true);
array_push($ratingsRaw, $tempArray);
}
I can not save every object with new variable (like $item1,$item2, etc...)
How can I store every object in one array and how can I get average of every rating type in one output object?
You can use AVG() method in your MySQL query and retrieve average value directly from database.
SELECT AVG(rating) AS avg_rating FROM ratings WHERE item_id='ABC123'
Or when you don't specify ID and you want average value for all items.
SELECT AVG(rating) AS avg_rating, item_id FROM ratings GROUP BY item_id
Related
I'm working on a PHP project with MYSQL database. I have a table of groups of students. Each group has an examiner. What i want to do is that i want to set two examiners for each group randomly. How to do it?
MySQL Code:
create table groups (
groupID int(10) not null,
nbStudents int not null,
avgGPA DOUBLE NOT NULL,
projectName varchar(50) not null,
advisorID int,
examiner1ID int,
examiner2ID int,
adminID int not null,
primary key (groupID)
);
create table faculty (
name varchar(30) not null,
facultyID int(10) not null,
email varchar(30) not null,
mobile int(15) not null,
primary key (facultyID)
);
examiner1ID and examiner2ID are foreign keys from the table faculty.
Here is a very convoluted way to do it. It uses 2 subqueries to pick faculty members, and insert .. on duplicate key to update the examiners IDs.
insert into groups
(groupID, examiner1ID, examiner2ID)
select groupID,
#x:=(select facultyID from faculty order by rand() limit 1),
(select facultyID from faculty where facultyID <> #x order by rand() limit 1)
from groups
on duplicate key update examiner1ID=values(examiner1ID), examiner2ID=values(examiner2ID);
#x is a user-defined-variable. In this case, it is used to store the first random faculty member. <> #x makes sure we don't pick the same faculty member in both slots.
Since groupID is a unique key, when we try to insert a row with an existing unique key, it will update the existing row instead of inserting it. That's what on duplicate key update clause is used for.
set different examiners for each group:
insert into groups
(groupID, examier1ID, examier2ID)
select a.groupID, max(if(b.id%2, b.facultyID, 0)), max(if(b.id%2, 0, b.facultyID))
from (
select #row:=#row+1 id, groupID
from groups a
join (select #row:=0) b) a
join (
select #row:=#row+1 id, facultyID
from (
select facultyID
from faculty a
order by rand()) a
join (select #row:=0) b) b on a.id = ceil(b.id/2)
group by a.groupID
on duplicate key update examiner1ID=values(examiner1ID), examiner2ID=values(examiner2ID);
I have 2 separate tables, both of which I need to query simultaneously to get the correct information to display. The tables are members and posts. Through an html form, a user enters criteria for the members table, and then I need to use the primary index of all those specific members to find all the posts submitted by those members and then do a sort on the posts table results. The results will be a mixture of rows from the two tables. Both tables have a primary index of the name 'id'. So far what I've come up with is:
$sql_get_posts = mysqli_query($link, "(SELECT id, username FROM members WHERE active='y' AND gender='M' AND city='Yuma' AND state='Arizona') UNION (SELECT * FROM posts WHERE member_id='id' AND active='y' ORDER BY list_weight DESC)") or die(mysqli_error($link));
The error I'm getting is "The used SELECT statements have a different number of columns".
I need to then cycle through the returned results from both tables to populate the content seen by the user:
<?php
while ($row = mysqli_fetch_array($sql_get_posts)) {
$post_id = $row['id']; //This should be the post primary index named 'id', not the member primary index also name 'id'
$member_id = $row['member_id']; //This is the member_id row in the post table referencing this particular member who wrote this post
$member_username = $row['username']; //This is a row stored in the member table
$title = $row['title']; //This is a row stored in post table
******//and on and on getting rows from only the post table
}
Edit My SQL tables:
CREATE TABLE IF NOT EXISTS `members` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`age` varchar(3) NOT NULL,
`gender` varchar(1) NOT NULL,
`city` varchar(20) NOT NULL,
`state` varchar(50) NOT NULL,
`active` enum('y','n') NOT NULL DEFAULT 'y',
`created_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
CREATE TABLE IF NOT EXISTS `posts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`member_id` int(11) NOT NULL,
`title` text NOT NULL,
`comments` enum('y','n') NOT NULL DEFAULT 'y',
`post_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`list_weight` double NOT NULL,
`active` enum('y','n') NOT NULL DEFAULT 'y',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=47 ;
Use join instead of union, union assumes the tables you're combining are similar, whereas join merges the columns of two tables.
Something like:
SELECT members.id, members.username, posts.*
FROM members
INNER JOIN posts
ON members.id = posts.member_id
WHERE members.active='y' AND members.gender='M' AND members.city='Yuma' AND members.state='Arizona'
ORDER BY posts.list_weight DESC
SELECT statement within the UNION must have the same number of columns. The columns must also have similar data types. Also, the columns in each SELECT statement must be in the same order.
But you have selected only 2 columns in first query and "*" for second select query
use joins
SELECT m.id, m.username,p.* FROM members m JOIN posts p on m.active='y' AND m.gender='M' AND m.city='Yuma' AND m.state='Arizona' and p.member_id='id' AND p.active='y' ORDER BY p.list_weight DESC
or you can use
SELECT m.id, m.username,p.* FROM members m,posts p where m.active='y' AND m.gender='M' AND m.city='Yuma' AND m.state='Arizona' and p.member_id='id' AND p.active='y' ORDER BY p.list_weight DESC
I have a large database with EAV structured data that has to be searchable and pageable. I tried every trick in my book to get it fast enough, but under certain circumstances, it still fails to complete in a reasonable time.
This is my table structure (relevant parts only, ask away if you need more):
CREATE TABLE IF NOT EXISTS `object` (
`object_id` bigint(20) NOT NULL AUTO_INCREMENT,
`oid` varchar(32) CHARACTER SET utf8 NOT NULL,
`status` varchar(100) CHARACTER SET utf8 DEFAULT NULL,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`object_id`),
UNIQUE KEY `oid` (`oid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `version` (
`version_id` bigint(20) NOT NULL AUTO_INCREMENT,
`type_id` bigint(20) NOT NULL,
`object_id` bigint(20) NOT NULL,
`created` datetime NOT NULL,
`status` varchar(100) CHARACTER SET utf8 DEFAULT NULL,
PRIMARY KEY (`version_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `value` (
`value_id` bigint(20) NOT NULL AUTO_INCREMENT,
`object_id` int(11) NOT NULL,
`attribute_id` int(11) NOT NULL,
`version_id` bigint(20) NOT NULL,
`type_id` bigint(20) NOT NULL,
`value` text NOT NULL,
PRIMARY KEY (`value_id`),
KEY `field_id` (`attribute_id`),
KEY `action_id` (`version_id`),
KEY `form_id` (`type_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
This is a sample object. I have around 1 million of those in my database. each object may have different number of attributes with different attribute_id
INSERT INTO `owner` (`owner_id`, `uid`, `status`, `created`, `updated`) VALUES (1, 'cwnzrdxs4dzxns47xs4tx', 'Green', NOW(), NOW());
INSERT INTO `object` (`object_id`, `type_id`, `owner_id`, `created`, `status`) VALUES (1, 1, 1, NOW(), NOW());
INSERT INTO `value` (`value_id`, `owner_id`, `attribute_id`, `object_id`, `type_id`, `value`) VALUES (1, 1, 1, 1, 1, 'Munich');
INSERT INTO `value` (`value_id`, `owner_id`, `attribute_id`, `object_id`, `type_id`, `value`) VALUES (2, 1, 2, 1, 1, 'Germany');
INSERT INTO `value` (`value_id`, `owner_id`, `attribute_id`, `object_id`, `type_id`, `value`) VALUES (3, 1, 3, 1, 1, '123');
INSERT INTO `value` (`value_id`, `owner_id`, `attribute_id`, `object_id`, `type_id`, `value`) VALUES (4, 1, 4, 1, 1, '2012-01-13');
INSERT INTO `value` (`value_id`, `owner_id`, `attribute_id`, `object_id`, `type_id`, `value`) VALUES (5, 1, 5, 1, 1, 'A cake!');
Now on to my current mechanism. My first try was the typical approach to Mysql. Do one huge SQL with loads of joins on anything I require. Complete desaster! Took way to long to load and even crashed the PHP and MySQL servers due to exhausted RAM.
So I split my queries up into several steps:
1 Determine all needed attribute_ids.
I can look them up in another table that references the type_id of an object. The result is a list of attribute_ids. (this table is not very relevant to the performance, so it's not included in my sample.)
:type_id contains all type_ids from any objects I want to include in my search. I already got this information in my application. So this is inexpensive.
SELECT * FROM attribute WHERE form_id IN (:type_id)
Result is an array of type_id integers.
2 Search for matching objects
A big SQL query is compiled that adds one INNER JOIN for each and every condition I want. This sounds horrible, but in the end, it was the fastest method :(
A typical generated query might look like this. The LIMIT sadly is necessary or I will potentially get so many IDs that the resulting array makes PHP explode or break the IN statement in the next Query:
SELECT DISTINCT `version`.object_id FROM `version`
INNER JOIN `version` AS condition1
ON `version`.version_id = condition1.version_id
AND condition1.created = '2012-03-04' -- Filter by version date
INNER JOIN `value` AS condition2
ON `version`.version_id = condition2.version_id
AND condition2.type_id IN (:type_id) -- try to limit joins to object types we need
AND condition2.attribute_id = :field_id2 -- searching for a value in a specific attribute
AND condition2.value = 'Munich' -- searching for the value 'Munich'
INNER JOIN `value` AS condition3
ON `version`.version_id = condition3.version_id
AND condition3.type_id IN (:type_id) -- try to limit joins to object types we need
AND condition3.attribute_id = :field_id3 -- searching for a value in a specific attribute
AND condition3.value = 'Green' -- searching for the value 'Green'
WHERE `version`.type_id IN (:type_id) ORDER BY `version`.version_id DESC LIMIT 10000
The result will contain all object_ids from any object I might need. I am selecting object_ids and not version_ids as I need to have all versions of matching objects, regardless of which version matched.
3 Sort and page results
Next I will create a query that sorts the objects by a certain attribute and then pages the resulting array.
SELECT DISTINCT object_id
FROM value
WHERE object_id IN (:foundObjects)
AND attribute_id = :attribute_id_to_sort
AND value > ''
ORDER BY value ASC LIMIT :limit OFFSET :offset
The result is a sorted and paged list of object ids from former search
4 Get our complete objects, versions and attributes
In the last step, I will select all values for any objects and versions the former queries found.
SELECT `value`.*, `object`.*, `version`.*, `type`.*
`object`.status AS `object.status`,
`object`.flag AS `object.flag`,
`version`.created AS `version.created`,
`version`.status AS `version.status`,
FROM version
INNER JOIN `type` ON `version`.form_id = `type`.type_id
INNER JOIN `object` ON `version`.object_id = `object`.object_id
LEFT JOIN value ON `version`.version_id = `value`.version_id
WHERE version.object_id IN (:sortedObjectIds) AND `version.type_id IN (:typeIds)
ORDER BY version.created DESC
The result will then be compiled via PHP into nice object->version->value array structures.
Now the question:
Can this whole mess be accelerated in any way?
Can I somehow remove the LIMIT 10000 restriction from my search query?
If all else fails, maybe switch database technology? See my other question: Database optimized for searching in large number of objects with different attributes
Real Life samples
Table sizes: object - 193801 rows, version - 193841 rows, value - 1053928 rows
SELECT * FROM attribute WHERE attribute_id IN (30)
SELECT DISTINCT `version`.object_id
FROM version
INNER JOIN value AS condition_d4e328e33813
ON version.version_id = condition_d4e328e33813.version_id
AND condition_d4e328e33813.type_id IN (30)
AND condition_d4e328e33813.attribute_id IN (377)
AND condition_d4e328e33813.value LIKE '%e%'
INNER JOIN value AS condition_2c870b0a429f
ON version.version_id = condition_2c870b0a429f.version_id
AND condition_2c870b0a429f.type_id IN (30)
AND condition_2c870b0a429f.attribute_id IN (376)
AND condition_2c870b0a429f.value LIKE '%s%'
WHERE version.type_id IN (30)
ORDER BY version.version_id DESC LIMIT 10000 -- limit to 10000 or it breaks!
Explain:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE condition_2c870b0a429f ref field_id,action_id,form_id field_id 4 const 178639 Using where; Using temporary; Using filesort
1 SIMPLE action eq_ref PRIMARY PRIMARY 8 condition_2c870b0a429f.action_id 1 Using where
1 SIMPLE condition_d4e328e33813 ref field_id,action_id,form_id action_id 8 action.action_id 11 Using where; Distinct
objects search completed (Peak RAM: 5.91MB, Time: 4.64s)
SELECT DISTINCT object_id
FROM version
WHERE object_id IN (193793,193789, ... ,135326,135324) -- 10000 ids in here!
ORDER BY created ASC
LIMIT 50 OFFSET 0
objects sort completed (Peak RAM: 6.68MB, Time: 0.352s)
SELECT `value`.*, object.*, version.*, type.*,
object.status AS `object.status`,
object.flag AS `object.flag`,
version.created AS `version.created`,
version.status AS `version.status`,
version.flag AS `version.flag`
FROM version
INNER JOIN type ON version.type_id = type.type_id
INNER JOIN object ON version.object_id = object.object_id
LEFT JOIN value ON version.version_id = `value`.version_id
WHERE version.object_id IN (135324,135326,...,135658,135661) AND version.type_id IN (30)
ORDER BY quality DESC, version.created DESC
objects load query completed (Peak RAM: 6.68MB, Time: 0.083s)
objects compilation into arrays completed (Peak RAM: 6.68MB, Time: 0.007s)
Just try to add an EXPLAIN before your search query :
EXPLAIN SELECT DISTINCT `version`.object_id FROM `version`, etc ...
then check the results in the "Extra" column, it will give you some clues to speedup your query, like adding INDEX on the right fields.
Also some times you can removeINNER JOIN, get more results in your Mysql response and filter the big array by processing with PHP loops.
I would start by trying to have covering indexes (ie: all columns to match the criteria you are querying on and even pulling out as result). This way the engine does not have to go back to the raw page data.
Since you need the "object_id" from version, and using the "version_id" as join basis to the other tables. Your version table also has a WHERE clause on the TYPE_ID, so I would have an index on
version table -- (object_id, version_id, type_id)
For your "value" table, match there too for criteria
value table -- ( version_id, type_id, attribute_id, value, created )
How can I compile the results of various calculations from data gathered from different tables ? Present summaries in php table. I have people in a table that is linked to two other tables. It will be sorted by department how many people are women and men in number and percentage . How much they earn in total per department in average. How big women's pay is a percentage of men and how great the wage distribution is the max and min values for men and women.
These code is used to get all the data.
I have added some value manually just for clarification in this picture.
I have struggled with this for some time now and see no solution. Grateful for the help !
Please see below, it's not exactly as you need, since I don't know a structure of tables, but you will get an idea:
CREATE TABLE `usergroup` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sex` enum('yes','no') NOT NULL DEFAULT 'yes',
`salary` decimal(10,2) DEFAULT NULL,
`userGroupId` int(11) DEFAULT NULL,
`name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into `usergroup`(`id`,`name`) values (1,'Sales');
insert into `usergroup`(`id`,`name`) values (2,'Support');
insert into `usergroup`(`id`,`name`) values (3,'Managment');
insert into `usergroup`(`id`,`name`) values (4,'Others');
insert into `user`(`id`,`sex`,`salary`,`userGroupId`,`name`) values (1,'yes',20000.00,1,'Scott');
insert into `user`(`id`,`sex`,`salary`,`userGroupId`,`name`) values (2,'yes',30000.00,1,'Peter');
insert into `user`(`id`,`sex`,`salary`,`userGroupId`,`name`) values (3,'no',20000.00,1,'Mike');
insert into `user`(`id`,`sex`,`salary`,`userGroupId`,`name`) values (4,'yes',100000.00,2,'Senior');
insert into `user`(`id`,`sex`,`salary`,`userGroupId`,`name`) values (5,'no',50000.00,2,'Junior');
insert into `user`(`id`,`sex`,`salary`,`userGroupId`,`name`) values (6,'yes',75000.00,2,'Middle');
insert into `user`(`id`,`sex`,`salary`,`userGroupId`,`name`) values (7,'yes',250000.00,3,'King');
insert into `user`(`id`,`sex`,`salary`,`userGroupId`,`name`) values (8,'yes',300000.00,3,'Ace');
insert into `user`(`id`,`sex`,`salary`,`userGroupId`,`name`) values (9,'no',200000.00,3,'Queen');
insert into `user`(`id`,`sex`,`salary`,`userGroupId`,`name`) values (10,'yes',100000.00,3,'Jack');
insert into `user`(`id`,`sex`,`salary`,`userGroupId`,`name`) values (11,'no',400000.00,3,'LadyJoker');
Query to get report. The subquery is selecting a data with stats values, which are used in the outer query for calculation:
select analytic.userGroupId,
analytic.name,
analytic.countMen,
analytic.countWomen,
round((analytic.countMen / analytic.countMisc)*100, 2) as percentMen,
round((analytic.countWomen / analytic.countMisc)*100, 2) as percentWomen,
round((analytic.maxSalaryMen + analytic.minSalaryMen)/2, 2) as basicSalaryMen,
round((analytic.maxSalaryWomen + analytic.minSalaryWomen)/2, 2) as basicSalaryWomen,
round(
(analytic.maxSalaryMen + analytic.minSalaryMen +
analytic.maxSalaryWomen + analytic.minSalaryWomen
)/4,
2)
as basicSalaryMisc,
if(analytic.maxSalaryMen + analytic.minSalaryMen = 0, 100,
round((analytic.maxSalaryWomen + analytic.minSalaryWomen)/(analytic.maxSalaryMen + analytic.minSalaryMen), 2)
) as salaryBasicWomenPercentOfMen,
analytic.maxSalaryMen,
analytic.minSalaryMen,
analytic.maxSalaryWomen,
analytic.minSalaryWomen
from (
select ug.id as userGroupId,
ug.name,
sum(if(u.sex='yes', 1, 0)) countMen,
sum(if(u.sex='no', 1, 0)) countWomen,
count(*) countMisc,
max(if(u.sex='yes', u.salary, null)) maxSalaryMen,
min(if(u.sex='yes', u.salary, null)) minSalaryMen,
max(if(u.sex='no', u.salary, null)) maxSalaryWomen,
min(if(u.sex='no', u.salary, null)) minSalaryWomen
from userGroup ug left join user u
on u.userGroupId = ug.id
group by ug.id, ug.name
order by ug.name
) analytic
I'm dealing with this problem. There is tableorders(oid,datetime,quantity,title,username,mid).
The table orders is updated from php code as far as the features oid,datetime,quantity,title,username are concerned. The problem is that I want to classify each entry based on both datetime and username so as to gather these entries under an order code in order to make an ordering entry. (I can't think of anything else at the moment).
The question is how can I select those entries that are corresponding to the same username and the same date time.
For example the if I have 3entries (freddo espresso,latte,freddoccino) belong to the same order procedure (are posted by the same username, tha exact same datetime) and I need to present them to my user as a completed order.
Here is the structure of table orders:
CREATE TABLE IF NOT EXISTS `orders` (
`oid` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`datetime` DATETIME NOT NULL,
`quantity` INT NOT NULL,
`sum` FLOAT(4,2) NOT NULL,
`title` VARCHAR(30) COLLATE utf8_unicode_ci NOT NULL,
`username` VARCHAR(30) COLLATE utf8_unicode_ci NOT NULL,
`mid` VARCHAR(30) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`oid`),
KEY `username`(`username`,`mid`,`title`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=10000;
The feature title is foreign key from table products:
CREATE TABLE IF NOT EXISTS `products`(
`title` VARCHAR(30) COLLATE utf8_unicode_ci NOT NULL,
`descr` TEXT(255),
`price` FLOAT(4,2) NOT NULL,
`popularity` INT NOT NULL,
`cname` VARCHAR(20) COLLATE utf8_unicode_ci NOT NULL,
`mid` VARCHAR(30) COLLATE utf8_unicode_ci NOT NULL ,
PRIMARY KEY(`title`),
KEY `cname` (`cname`, `mid`)
)ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=10000;
Sorry If I'm a little uncomprehensive, though I really need some help to come to a conclusion. Any ideas?
Thanks in advance!
If you know what the datetime value and the username values are then you can simply use:
SELECT * FROM orders WHERE username = '$username' AND datetime = '$datetime'
However, what you would be better off doing is splitting this into two separate tables; something like:
Orders
OrderID
OrderTime
UserName
Items
ItemID
OrderID
Title
Then you would search in the following way:
SELECT Orders.OrderID, Orders.UserName, Items.Title
FROM Orders
INNER JOIN Items ON Orders.OrderID = Items.OrderID
WHERE
Orders.UserName = '$username'
AND
Orders.OrderDate = '$datetime'
When adding orders you add a record to Orders first, and then use that OrderID and add it to each item inserted in Items...
Insert Example
$mysqli; //Assuming your connection to the database...
$items; //Assuming an array of items for the order like: array('Coffee', 'Tea')
$username; //Assuming the user name to be inserted for the order
$mysqli->query("INSERT INTO Orders(`OrderTime`, `UserName`) VALUES(NOW(), '$username')");
$orderid = $mysqli->insert_id;
foreach($items as $item){
$mysqli->query("INSERT INTO Items (`OrderID`, `Title`) VALUES($orderid, '$title')");
}
NOTE: You should make sure to sanitize data before inserting to database...
Storing JSON
Storing JSON in a database is going to require you to make sure that you use a field data type that is an appropriate length (e.g. a blob).
You mentioned that you retrieve the titles as an array from a form so I'm now going to refer to that as $titles.
Saving to database
$username = '...'; // Username or id to store in database with order
$titles = array(.....); // Array of titles from form
$encodedTitles = json_encode($titles); // Convert to JSON
$mysqli->query("INSERT INTO table_name (titles_field, username_field, date_field) VALUES ('$titles', '$username', NOW())"); // Save to database (assuming already open connection
Retrieve from database
$result = $mysqli->query("SELECT titles FROM table_name WHERE username = 'username_value' AND date_field = 'date_value'"); //Run query to get row
$row = $result->fetch_assoc(); // Fetch row
$titles = json_decode($row['titles']); // This is the same as the `titles` array from the from above!
SELECT quantity,title
FROM orders
WHERE username = ? and datetime = ?
Would return the quantity of items for a specific user on specific date. Instead of a date you could use an order id, which might be a bit safer. If you use order id, then username becomes irrelevant as well, since order ids should be unique.
The answer posted with the query will help you but you should also consider changing your table structure. Looks like you could have a table named orders and another one named orders_items. Then you could list all the itens from orders_itens matching a single order.
I think this query will return kind of data where you have the same unique_id string for records where username and datetime are the same.
SELECT MD5(a.unique_id), b.* FROM (
SELECT
GROUP_CONCAT(oid) unique_id, `datetime`, username
FROM `orders` GROUP BY username, `datetime`
) a
RIGHT JOIN `orders` b
ON a.`datetime` = b.`datetime` AND a.username = b.username
ORDER BY unique_id, oid;
I also have another answer for about 3 thousands characters long but I think this variant will help you more than my long tutorial how to split the table to two tables and how to migrate data into it + php code samples. So I decided not publicate it. )))
Edit: I think you even can run this one query which is easiest and works faster:
SELECT *, MD5( CONCAT( `username` , `datetime` ) ) unique_id
FROM `orders`
ORDER BY unique_id, oid;