CodeIgniter: Join more than 3 tables - php

I have five tables:
Bill: no(pk),date, time, total
BillOrderRelation: no(fk), order_id(fk)
Order: order_id(pk), menu_id(fk), quantities, total
Menu: menu_id(pk), category_id(fk), menu_name, stock, price
Category: category_id(pk), category_name, colour
In my case, I have to retrieve which menu that has a highest sales in one day range, 7 days range, and 30 days range.
I've already succeed retrieve those information, but i think it's too complicated. First I have to retrieve the date on Bill, and then find the order in BillOrderRelation, and then find the Menu, and find the Category name. It includes a lot of queries and complex way to do the summing stuff for the same menu.
My question is, is that possible to query all those table in one query to retrieve just menu.menu_name, order.quantities, order.total, category.name and it's included the sum stuff for the same menu retrieved?
I've already succeed make a query for three table without using time range like this..
SELECT
menu.menu_name as top_item,
SUM(order.quantities) AS count_sold,
SUM(order.total) AS amount,
category.nama AS categories
FROM
menu, order, category
WHERE
menu.mennu_id = bill.menu_id
AND category.category_id = menu.category_id
GROUP BY
bill.menu_id, menu.menu_name, category.category_name
ORDER BY
count_sold DESC
Is there any tricky way for the case above?

Update for PostgreSQL
I believe you want something like this:
SELECT
m.menu_name AS top_item
, c.name AS category
, SUM(o.quantities) AS sum_quantity
, SUM(o.total) AS sum_total
FROM
menu m
JOIN
category c
ON c.category_id=m.category_id
JOIN
order o
ON o.menu_id=m.menu_id
JOIN
billorderrelation bor
ON bor.order_id=o.order_id
JOIN
bill b
ON b.no=bor.no
WHERE
b.date >= (CURRENT_DATE - INTERVAL '7 days')
GROUP BY
m.menu_id
ORDER BY
sum_quantity DESC
;
CURRENT_DATE allows you to get the current date (according to the timezone specified in your database). Read more:
https://www.postgresql.org/docs/8.2/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT

Related

mysql calculate percentage between two sub queries

I am trying to work out the percentage of a number of students who meet certain criteria.
I have 3 separate tables that I need to get data from, and then I need to get the total from one table (student) as the total of students.
Then I need to use this total, to divide the COUNT of the no of students in the 2nd query.
So basically I am trying to get a count of ALL the students that are in the DB first.
Then count the no of students that appear in my main query (the one returning the data).
Then I need to perform the calculation that will take the noOfStudents (2) and divide by the main total (24) (no of students in DB) then *100 to give me the percentage of students who have met the criteria in the main query.
This is what I have so far:
SELECT * FROM (
(
SELECT s.firstname, s.lastname, s.RegistrationDate, s.Email, d.ReviewDate,(r.description) AS "Viva" , COUNT(*) AS "No of Students"
FROM student s
INNER JOIN dates d
ON s.id=d.student_identifier
INNER JOIN reviews r
ON d.review_Identifier=r.id
WHERE r.description = "Viva Date"
GROUP BY s.student_identifier
ORDER BY s.student_identifier)
) AS Completed
WHERE Completed.ReviewDate BETWEEN '2012-01-01' AND '2014-12-01'
;
I need to output the fields following the second SELECT and this data in turn will be displayed via PHP/HTML code on a page (the BETWEEN dates will be sent via '%s').
I wondered if I should be using 2 separate queries and then getting the value (24) from the first query to perform the calculation in the second query, but I have not been able to work out how to save as 2 separate queries and then reference the first query.
I am also not sure if it is possible to display an overall % total at the same time as outputting the individual rows that meet the criteria?
I am trying to teach myself SQL, so I apologise if I have made any glaring mistakes/assumptions in any of the above, and would appreciate any advice that's out there.
Thank you.
Could you do this?
SELECT COUNT(*) as TotalPopulation,
COUNT(d.student_identifier='student') as TotalStudents,
COUNT(d.student_identifier='student')/ count(*) *100 as Percentage of students
from students s
inner join dates d
on s.id = d.student_identifier
inner join reviews r
on r.id = d.review_Identifier
WHERE d.ReviewDate BETWEEN '2012-01-01' AND '2014-12-01' and r.description = 'Viva Date';
You do not need first name last name if you are just looking for counts, necessarily.
This get's the count(*) of table, then whatever flag you use to identify a student in the second count(), you just had it grouped by before, which could give you wrong results considering there's much else in your select before aggregation.
You could also try:
SELECT d.student_identifier, s.firstname, s.lastname,
s.RegistrationDate, s.Email, d.ReviewDate,(r.description) AS "Viva"
FROM student s
INNER JOIN dates d
ON s.id=d.student_identifier
INNER JOIN reviews r
ON d.review_Identifier=r.id
WHERE r.description = "Viva Date" and d.ReviewDate BETWEEN '2012-01-01' AND '2014-12-01'
ORDER BY s.student_identifier
Now, if you want to return a list, that's the second one, if you want to return a count, you would use the first query and adjust to your student_identifier.

AVG and COUNT functions are not giving proper result in SQL query

I have following query and I am trying to display members in the order where members with highest average rating will be displayed first, if more than one members have same average rating then highest number of rating will be considered.
Here,
Member A has been rated by 3 visitors and average rating value is 5 while
Member B has been rated by 2 visitors and average rating value is 5
So according to below query, Member A should display first because he has 5 average rating and rated by 3 persons while Member B should display on second position.
But Member B is displaying first and Member A is displaying second so this is problem. Please let me know what wrong I am doing in query.
SELECT m.*,mc.*
FROM t_member m
LEFT JOIN tr_member_category mc ON m.memberpkid=mc.memberpkid
LEFT JOIN tr_comment c
ON m.memberpkid=c.memberpkid
AND c.approved='YES' AND c.visible='YES'
WHERE m.visible='YES' AND m.approved='YES'
AND m.gender='FEMALE' AND mc.archivecatpkid=1
GROUP BY m.memberpkid
ORDER BY avg(c.ratingvalue) DESC, COUNT(c.ratingvalue) DESC
Thank you very much in advance,
KRA
Move the AVG and COUNT functions out of the ORDER clause, into the SELECT clause. Give them a good name and then order on those.
i.e.
SELECT m.*, mc.*, AVG(c.ratingvalue) AS average_rating, COUNT(c.ratingvalue) AS number_of_ratings
FROM t_member m
LEFT JOIN tr_member_category mc ON m.memberpkid=mc.memberpkid
LEFT JOIN tr_comment c
ON m.memberpkid=c.memberpkid
AND c.approved='YES' AND c.visible='YES'
WHERE m.visible='YES' AND m.approved='YES'
AND m.gender='FEMALE' AND mc.archivecatpkid=1
GROUP BY m.memberpkid
ORDER BY average_rating DESC, number_of_ratings DESC
EDIT: remember that an aggregate function returns a value. If you put them in an ORDER BY clause, it reads "order by this value", as opposed to "order by this column". Keep that in mind whilst writing SQL.

Trying to use IF with MAX in MYSQL to get most recent date between 2 tables

I've got two tables (MySQL), one with a list of categories and another with a list of products which can be assigned to a category. I need to pull out the date of the most recent change across both tables so I can reset the cache for this page if needed.
I seem to be struggling with using MAX() in a CASE, currently I have this which works but the product date is just the latest one entered into the database and not necessarily the most recent.
SELECT c.pageid as caturl, p.updated as pup, c.updated as dup,
CASE WHEN p.updated > c.updated THEN p.updated ELSE c.updated END AS latest
FROM products p, categories c
WHERE p.catid = c.id AND c.hide=0
GROUP BY c.title
When I try to use MAX() within the CASE it throws an error, as it does when using an IF.
I'd appreciate any help, thanks.
Ok, I'm taking a stab at this, two derived tables.
One, that calculate the MostRecent updated date for each category in the categories table: "catdates" (and also returns the pageid)
The Other, calculates the MostRecent updated date across all products in a given category for each category in the products table: "proddates"
Then we just join on categoryid and take the higher value.
SELECT c.pageid,
GREATEST(proddate.LastModified,catdates.LastModified) AS LastModified
from
(SELECT p.catid as catid,MAX(p.updated) AS LastModified
FROM products p
GROUP BY p.id,p.catid) proddates
INNER JOIN
(SELECT c.id AS catid,c.pageid,MAX(c.updated) AS LastModified
FROM categories c
WHERE c.hide=0
GROUP BY c.id,c.pageid) catdates
ON proddates.catid=catdates.catid
If you only want the latest rows, something like:
SELECT
c.pageid as caturl
, p.updated as pup
, c.updated as dup
,GREATEST(p.updated,c.updated) AS latest
FROM products p
INNER JOIN categories c ON (p.catid = c.id)
WHERE c.hide=0
GROUP BY caturl
HAVING lastest = MAX(lastest)
The greatest function negates the need for the case when, although it does not change the query.
The real change is in the having clause that selects only the latest rows.

Most Popular Mysql Row over last 7 days

In one table I have
ID, PAGE_ID, DATE
Each time a page is loaded, the DATE, PAGE_ID [from the page table below] are loaded into the table above.
I am trying to calculate and sort pages by popularity. The page table contains:
ID [PAGE_ID], DESCRIPTION, DATE
I have no idea where to start.
select L.PAGE_ID, P.DESCRIPTION, count(L.ID) from LOADED_PAGE L
inner join PAGE P on P.ID = L.PAGE_ID
where L.DATE > :sevenDaysAgo
group by L.PAGE_ID, P.DESCRIPTION
order by count(L.ID) desc
will give you the list of loaded pages, from the most popular to the least one.
select
id_page,
count(*) as popularity
from table
where date >= curdate() - interval 7 day
group by id_page
order by popularity desc

Get multiple GROUP BY results per group, or use separate concatenated table

I am working on an auction web application. Now i have a table with bids, and from this table i want to select the last 10 bids per auction.
Now I know I can get the last bid by using something like:
SELECT bids.id FROM bids WHERE * GROUP BY bids.id ORDER BY bids.created
Now I have read that setting an amount for the GROUP BY results is not an easy thing to do, actually I have found no easy solution, if there is i would like to hear that.
But i have come up with some solutions to tackle this problem, but I am not sure if i am doing this well.
Alternative
The first thing is creating a new table, calling this bids_history. In this table i store a string of the last items.
example:
bids_history
================================================================
auction_id bid_id bidders times
1 20,25,40 user1,user2,user1 time1,time2,time3
I have to store the names and the times too, because I have found no easy way of taking the string used in bid_id(20,25,40), and just using this in a join.
This way i can just just join on auction id, and i have the latest result.
Now when there is placed a new bid, these are the steps:
insert bid into bids get the lastinserteid
get the bids_history string for this
auction product
explode the string
insert new values
check if there are more than 3
implode the array, and insert the string again
This all seems to me not a very well solution.
I really don't know which way to go. Please keep in mind this is a website with a lot of bidding's, they can g up to 15.000 bidding's per auction item. Maybe because of this amount is GROUPING and ORDERING not a good way to go. Please correct me if I am wrong.
After the auction is over i do clean up the bids table, removing all the bids, and store them in a separate table.
Can someone please help me tackle this problem!
And if you have been, thanks for reading..
EDIT
The tables i use are:
bids
======================
id (prim_key)
aid (auction id)
uid (user id)
cbid (current bid)
created (time created)
======================
auction_products
====================
id (prim_key)
pid (product id)
closetime (time the auction closses)
What i want as the result of the query:
result
===============================================
auction_products.id bids.uid bids.created
2 6 time1
2 8 time2
2 10 time3
5 3 time1
5 4 time2
5 9 time3
7 3 time1
7 2 time2
7 1 time3
So that is per auction the latest bids, to choose by number, 3 or 10
Using user variable, and control flow, i end up with that (just replace the <=3 with <=10 if you want the ten auctions) :
SELECT a.*
FROM
(SELECT aid, uid, created FROM bids ORDER BY aid, created DESC) a,
(SELECT #prev:=-1, #count:=1) b
WHERE
CASE WHEN #prev<>a.aid THEN
CASE WHEN #prev:=a.aid THEN
#count:=1
END
ELSE
#count:=#count+1
END <= 3
Why do this in one query?
$sql = "SELECT id FROM auctions ORDER BY created DESC LIMIT 10";
$auctions = array();
while($row = mysql_fetch_assoc(mysql_query($sql)))
$auctions[] = $row['id'];
$auctions = implode(', ', $auctions);
$sql = "SELECT id FROM bids WHERE auction_id IN ($auctions) ORDER BY created LIMIT 10";
// ...
You should obviously handle the case where, e.g. $auctions is empty, but I think this should work.
EDIT: This is wrong :-)
You will need to use a subquery:
SELECT bids1.id
FROM ( SELECT *
FROM bids AS bids1 LEFT JOIN
bids AS bids2 ON bids1.created < bids2.created
AND bids1.AuctionId = bids2.AuctionId
WHERE bid2.id IS NULL)
ORDER BY bids.created DESC
LIMIT 10
So the subquery performs a left join from bids to itself, pairing each record with all records that have the same auctionId and and a created date that is after its own created date. For the most recent record, there will be no other record with a greater created date, and so that record would not be included in the join, but since we use a Left join, it will be included, with all the bids2 fields being null, hence the WHERE bid2.id IS NULL statement.
So the sub query has one row per auction, contianing the data from the most recent bid. Then simply select off the top ten using orderby and limit.
If your database engine doesn't support subqueries, you can use a view just as well.
Ok, this one should work:
SELECT bids1.id
FROM bids AS bids1 LEFT JOIN
bids AS bids2 ON bids1.created < bids2.created
AND bids1.AuctionId = bids2.AuctionId
GROUP BY bids1.auctionId, bids1.created
HAVING COUNT(bids2.created) < 9
So, like before, left join bids with itself so we can compare each bid with all the others. Then, group it first by auction (we want the last ten bids per auction) and then by created. Because the left join pairs each bid with all previous bids, we can then count the number of bids2.created per group, which will give us the number of bids occurring before that bid. If this count is < 9 (because the first will have count == 0, it is zero indexed) it is one of the ten most recent bids, and we want to select it.
To select last 10 bids for a given auction, just create a normalized bids table (1 record per bid) and issue this query:
SELECT bids.id
FROM bids
WHERE auction = ?
ORDER BY
bids.created DESC
LIMIT 10
To select last 10 bids per multiple auctions, use this:
SELECT bo.*
FROM (
SELECT a.id,
COALESCE(
(
SELECT bi.created
FROM bids bi
WHERE bi.auction = a.id
ORDER BY
bi.auction DESC, bi.created DESC, bi.id DESC
LIMIT 1 OFFSET 9
), '01.01.1900'
) AS mcreated
COALESCE(
(
SELECT bi.id
FROM bids bi
WHERE bi.auction = a.id
ORDER BY
bi.auction DESC, bi.created DESC, bi.id DESC
LIMIT 1 OFFSET 9
), 0)
AS mid
FROM auctions a
) q
JOIN bids bo
ON bo.auction >= q.auction
AND bo.auction <= q.auction
AND (bo.created, bo.id) >= (q.mcreated, q.mid)
Create a composite index on bids (auction, created, id) for this to work fast.

Categories