how can i select these in mysql - php

Imagine that there are two tables called: Customers, Orders.
In Customers we have rows:
id=1 name=Alex key=12
id=2 name=Bob key=13
and in the Orders we have:
id=1 device=phone status=ordered key=12
id=2 device=phone status=delivered key=12
id=3 device=memory status=ordered key=13
id=4 device=memory status=returned key=13
Now I want it to choose the last one for each one of them in the Customers from Orders. Like it should choose Alex's information and the last row that is inserted for him in the Orders table.
How can I make a query for this?

You can do this with aggregation and a join:
select o.*
from customers c left join
orders o
on c.key = o.key left join
(select o.key, max(id) as maxid
from orders o
group by o.key
) ok
on o.id = oo.id;
The aggregation gets the latest value.
Note that key is a reserved word in MySQL (see here). That makes it a lousy column name, so you should change it.

Sounds like you're looking for the group-wise (key) maximum (id) as explained at https://dev.mysql.com/doc/refman/5.6/en/example-maximum-column-group-row.html
I prefer (for no good reason) the LEFT JOIN variant over the uncorrelated sub-query.

Here is a way you can do it
select
c.*,
o.id,
o.device,
o.status
from customer c
join orders o on o.`key` = c.`key`
join (
select max(id) as id,`key` from orders
group by `key`
)o1 on o1.id=o.id
http://www.sqlfiddle.com/#!9/96885/2

Related

Mysql query inner join on most recent date (today, yesterday, or before)

I'm attempting to pull the latest pricing data from a table on an Inner Join. Prices get updated throughout the day but aren't necessary updated at midnight.
The following query works great when the data is updated on prices by the end of the day. But how do I get it to get yesterdays data if today's data is blank?
I'm indexing off of a column that is formatted like this date_itemnumber => 2015-05-22_12341234
SELECT h.*, collection.*, history.price
FROM collection
INNER JOIN h ON collection.itemid=h.id
INNER JOIN history ON collection.itemid=history.itemid
AND concat('2015-05-23_',collection.itemid)=history.date_itemid
WHERE h.description LIKE '%Awesome%'
Production Query time: .046 sec
To be clear, I want it to check for the most up to date record for that item. Regardless on if it is today, yesterday or before that.
SQLFiddle1
The following query gives me the desired results but with my production dataset it takes over 3 minutes to return results. As my dataset gets larger, it would take longer. So this can't be the most efficient way to do this.
SELECT h.*, collection.*, history.price
FROM collection
INNER JOIN h ON collection.itemid=h.id
INNER JOIN history ON collection.itemid=history.itemid
AND (select history.date_itemid from history WHERE itemid=collection.itemid GROUP BY date_itemid DESC LIMIT 1)=history.date_itemid
WHERE h.description LIKE '%Awesome%'
Production Query time: 181.140 sec
SQLFiddle2
SELECT x.*
FROM history x
JOIN
( SELECT itemid
, MAX(date_itemid) max_date_itemid
FROM history
-- optional JOINS and WHERE here --
GROUP
BY itemid
) y
ON y.itemid = x.itemid
AND y.max_date_itemid = x.date_itemid;
http://sqlfiddle.com/#!9/975f5/13
This should works:
SELECT h.*, collection.*, history.price
FROM collection
INNER JOIN h ON collection.itemid=h.id
INNER JOIN(
SELECT a.*
FROM history a
INNER JOIN
( SELECT itemid,MAX(date_itemid) max_date_itemid
FROM history
GROUP BY itemid
) b ON b.itemid = a.itemid AND b.max_date_itemid = a.date_itemid
) AS history ON history.itemid = collection.itemid
WHERE h.description LIKE '%Awesome%'
I don't know if this take a lot of execution time. Please do try it, since you might have more data in your tables it will be a good test to see the query execution time.
This is actually a fairly common problem in SQL, at least I feel like I run into it a lot. What you want to do is join a one to many table, but only join to the latest or oldest record in that table.
The trick to this is to do a self LEFT join on the table with many records, specifying the foreign key and also that the id should be greater or less than the other records' ids (or dates or whatever you're using). Then in the WHERE conditions, you just add a condition that the left joined table has a NULL id - it wasn't able to be joined with a more recent record because it was the latest.
In your case the SQL should look something like this:
SELECT h.*, collection.*, history.price
FROM collection
INNER JOIN h ON collection.itemid=h.id
INNER JOIN history ON collection.itemid=history.itemid
-- left join history table again
LEFT JOIN history AS history2 ON history.itemid = history2.itemid AND history2.id > history.id
-- filter left join results to the most recent record
WHERE history2.id IS NULL
AND h.description LIKE '%Awesome%'
This is another approach that cuts one inner join statement
select h.*,his.date_itemid, his.price from history his
INNER JOIN h ON his.itemid=h.id
WHERE his.itemid IN (select itemid from collection) AND h.description LIKE '%Awesome%' and his.id IN (select max(id) from history group by history.itemid)
you can try it here http://sqlfiddle.com/#!9/837a8/1
I am not sure if this is what you want but i give it a try
EDIT: modified
CREATE VIEW LatestDatesforIds
AS
SELECT
MAX(`history`.`date_itemid`) AS `lastPriceDate`,
MAX(`history`.`id`) AS `matchingId`
FROM `history`
GROUP BY `history`.`itemid`;
CREATE VIEW MatchDatesToPrices
AS
SELECT
`ldi`.`lastPriceDate` AS `lastPriceDate`,
`ldi`.`matchingId` AS `matchingId`,
`h`.`id` AS `id`,
`h`.`itemid` AS `itemid`,
`h`.`price` AS `price`,
`h`.`date_itemid` AS `date_itemid`
FROM (`LatestDatesforIds` `ldi`
JOIN `history` `h`
ON ((`ldi`.`matchingId` = `h`.`id`)));
SELECT c.itemid,price,lastpriceDate,description
FROM collection c
INNER JOIN MatchDatesToPrices mp
ON c.itemid = mp.itemid
INNER JOIN h ON c.itemid = h.id
Difficult to test the speed on such a small dataset but avoiding 'Group By' might speed things up. You could try conditionally joining the history table to itself instead of Grouping?
e.g.
SELECT h.*, c.*, h1.price
FROM h
INNER JOIN history h1 ON h1.itemid = h.id
LEFT OUTER JOIN history h2 ON h2.itemid = h.id
AND h1.date_itemid < h2.date_itemid
INNER JOIN collection c ON c.itemid = h.id
WHERE h2.id IS NULL
AND h.description LIKE '%Awesome%'
Changing this line
AND h1.date_itemid < h2.date_itemid
to actually work on a sequential indexed field (preferably unique) will speed things up too. e.g. order by id ASC

MySQL LEFT JOIN is causing "duplicate" rows

I have tables "customers" and "plans" and I want to list all customers regardless they have a plan or not. I'm using query to do it
SELECT customer.name, plan.goal
FROM customer
LEFT JOIN plan ON plan.customerid=customer.customerid
ORDER BY customer.name
I also want to see the goal (plan.goal) with the customer name. This works as long as customer does not have a plan or has a single plan. If customer has two or more plans then I get as many rows of customer name as there are plans.
What I want is customer.name and plan.goal from the latest plan. We can assume the higher value in plan.planid is the latest plan.
I guess I should use sub queries and INNER JOINS some how but I just don't get it right now...
SELECT c.name,
( SELECT p.goal
FROM plan p
WHERE p.customerid=c.customerid
AND NOT EXISTS ( SELECT 'a'
FROM plan p2
WHERE p2.customerid = p.customerid
AND p2.planid > p.planId
)
)
FROM customer c
I would think something like this would work:
SELECT customer.name, plan.goal
FROM customer c
inner join plan p on c.customerId = p.customerId
inner JOIN (
-- grabs the most recent plan per customer
select max(planId) as planId, customerId
from plan
group by customerId
) maxPlan on p.planId = maxPlan.planId
UNION
-- handles customers w/o a plan
select customer.name, null
from customer c
left join plan p on c.customerId = p.customerId
where p.customerId is null
ORDER BY customer.name
I think you should add a boolean/tinyint column to the plan table that says IsLatest or something like that. Then you could do:
SELECT customer.name, plan.goal
FROM customer
LEFT JOIN plan ON plan.customerid=customer.customerid
where testplan.islatest = 1 or testplan.islatest is null
ORDER BY customer.name
Also, I would stay away from the sub-query answers such as
select a from (select b from c where e=f) where g=h
as they don't often perform very well, besides being confusing to read.

Using multiple inner joins

I have four tables:
users, orders, orders_product and products.
They are connected to each other by foreign key
user tables contains: id, name, email and username.
product table contains: id, product_name, product_description and product_price
orders table contains: id, u_id(foreign key).
orders_product table contains: id, product_id(foreign key), order_id(foreign key).
Now I was trying to fetch the name of a user with the total price of a particular order that he has placed.
The maximum I could went for was something like this:
SELECT prod.order_id,
SUM(product_price) AS Total
FROM products
INNER JOIN
(SELECT orders.id AS order_id,
orders_product.product_id
FROM orders
INNER JOIN orders_product ON orders.id = orders_product.order_id
WHERE order_id=1) AS prod ON products.id = prod.product_id;
It showed me total price of a particular order. Now I have two questions:
Is that query correct. It looks like a very long query. Can the same result be achieved with a smaller one?
How to fetch the name of a user with the total price of a particular order that he has placed.
Hi some addition to #Gordon Linoff
your query seems ok.
if you store your price data in order_products it will be good and some benefit, one of these benefit is aggregation will be simple. Second benefit if product price change it will not affect to order.
Your query is correct for one order, but it can be improved:
Don't use a subquery unless necessary. In MySQL this introduces additional overhead.
You are only looking at one order, which seems on the light site. You should remove the where clause.
You should be using a group by because you want aggregation.
You need to join in the user table to get the name.
I also added table aliases (abbreviations for table names). This makes the query a bit more readable:
SELECT u.name, SUM(p.product_price) as Total
FROM orders_product op INNER JOIN
orders o
ON o.id = op.order_id INNER JOIN
products p
ON p.id = op.product_id INNER JOIN
users u
on o.userid = u.id
WHERE op.order_id = 1
GROUP BY u.name;
Your SQL is wrong. Because You want to calculate specific to user. But your SQL is specific to Order. Your SQL will give result for One Order. Please make it User Specific by giving user name or what ever is unique.

SQL count with two table

was wondering if someone could help a newbie out with some sql?
Right so basically I want to get all the CustomerID's associated with a certain countrycode I can get that by doing a simple query on the customer table however I then need to find out how many forms the customer has submitted in the order table.
so in summary I need to get a list of customerID's from the Customer table and count how many time they show up in the Order table.
What's the easiest way to go about this?
P.S. please ignore any data type / relationship issues with the image above, it's just an example.
Thanks.
Join the tables, filter on country code and group the results:
SELECT CustomerID, COUNT(*)
FROM query_test.customer JOIN query_test.Order USING (CustomerID)
WHERE query_test.customer.countrycode = ?
GROUP BY CustomerID
You could use
SELECT
c.customerID,
count(o.form_id) AS form_count
FROM
customer c INNER JOIN order o
ON c.customerID = o.customerID
WHERE
c.countrycode = ??
GROUP BY
c.customerID
If you have customers that don't have any orders, then you will want a left outer join:
SELECT c.CustomerID, COUNT(o.CustomerID) as NumOrders, COUNT(distinct FormID) as NumForms
FROM query_test.customer c LEFT JOIN
query_test.Order o
on c.CustomerID = o.CustomerId
WHERE c.countrycode = ?
GROUP BY c.CustomerID;
Note this counts both the number of orders and the number of forms.

How to list two joined tables info by name (some with name, some possible empty)

I have two tables like these
Invoice (invoice_no, invoice_date, customer_id, ...),
Customers (customer_id, customer_name, ...)
Now what I want to do is list invoices ordered by customer name.
SELECT b.customer_name, a.*
FROM Invoice a, Customers b
WHERE a.customer_id=b.customer_id
ORDER BY b.customer_name
but problem with this sql is that if there are invoices without customer_id,
how can I list those invoices first and invoices with customer_id by customer_name asc.
use LEFT JOIN instead.
"kinda" weird. How come there are some invoices that without customer? To whom are you issuing it? Anyway, here's the query.
SELECT a.*, b.* // SELECT only the columns you want
FROM Invoice a
LEFT JOIN Customers b
ON a.customer_ID = b.customer_ID
To fully gain knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
SELECT
a.*,
b.customer_name
FROM Invoice a
LEFT JOIN Customers b ON a.customer_ID = b.customerID
Use joins instead of FROM tablea, tableb.
Because this will fetch cartisian product from both tables unless you restrict them with WHERE
Use this
SELECT * FROM Invoice,customer where Invoice.customer_id=customer.customer_id ORDER BY IF(Invoice.customer_id is NULL , Invoice.customer_id, ~Invoice.customer_id) ASC

Categories