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.
Related
I have 3 tables offers countries and transactions and what I'm trying to do is extract the offers from a specific country and devices, I already do this with this formula:
SELECT
*
FROM countries a
INNER JOIN offers b ON a.id = b.offer_id
WHERE a.country = "US"
AND b.device = 'all'
UNION
SELECT
*
FROM countries a
INNER JOIN offers b ON a.id = b.offer_id
WHERE a.country = "US"
AND b.device LIKE 'iphone%';
This code works but now I want to exclude the offers with the same ref between the tables users and transactions
the way this should work is:
The user from X country enter on Y device.
The user select an offer according to his country and device.
The offer is registered via post back to transactions.
The user enter to the main page and not longer see the offer he already finish.
Right now i'm capable of do the 3 first steps but not the fourth step. Any help about how do this?
Here is an example of the tables: SQL Fiddle
Thanks in advance
EDIT
if I put all this on words would be something like:
Select all from *offers* where the **offer_id** match with **ID** from *countries* with the **country** "US" and also from *offers* have the **device** "iPhone" now exclude from *transactions* the offers with the **user** = "xxxxx"
I hope this explain a little bit what I'm trying to do
Can't see how the query is user-specific , but to exclude offers refered to from transactions you can use NOT EXISTS
SELECT *
FROM countries a
INNER JOIN offers b ON a.id=b.offer_id
WHERE a.country="US" AND b.device = 'all'
AND NOT EXISTS (SELECT 1 FROM transactions t WHERE t.offer = b.offer_Id)
You could solve it with a LEFT OUTER JOIN to the transactions table.
I added t.id IS NULL to the WHERE-clause. So your result will just display offers which have no transaction.
SELECT *
FROM countries a
INNER JOIN offers b
ON a.id = b.offer_id
LEFT OUTER JOIN transactions t
ON t.id = b.offer_id
WHERE a.country = "US"
AND t.id IS NULL
Regarding your question it is still unclear for me how the tables users & countries are connected. This is an important detail, as the LEFT OUTER JOIN should just grab transactions from this specific user. So the condition transactions.user = user.ref should be added to the join too.
Will update the answer whenever you explain me this link
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
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
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.
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