Using MAX date to JOIN tables and get column value - mysql

I am trying to get the tracking number from a customers most recent order, but I am having trouble using MAX.
This just keeps returning nothing, even though I know table2 has values in there with dates. What's wrong with my query?
SELECT
t1.Invoice_Num,
t1.Tracking_Num
FROM
table1 t1
JOIN
table2 t2a on t1.Invoice_Num = t2a.Invoice_Num
JOIN (
SELECT
t2b.Invoice_Num,
MAX(t2b.Invoice_Date) Last_Sale
FROM
table2 t2b
WHERE
t2b.Customer_Num = 'cust1'
GROUP BY t2b.Invoice_Num
) LS
on t1.Invoice_Num = LS.Invoice_Num
--------------------------------------------------
Table1
+-------------+--------------+
| Invoice_Num | Tracking_Num |
+-------------+--------------+
| abc123 | 12345678 |
| def456 | 87654321 |
+-------------+--------------+
Table2
+-------------+--------------+--------------+
| Invoice_Num | Customer_Num | Invoice_Date |
+-------------+--------------+--------------+
| abc123 | cust1 | 10/25/2017 |
| def456 | cust1 | 10/24/2017 |
+-------------+--------------+--------------+
Desired output is -
+-------------+--------------+
| Invoice_Num | Tracking_Num |
+-------------+--------------+
| abc123 | 12345678 |
+-------------+--------------+
based on the most recent Invoice_Date of cust1

Here's a generic alternative approach that can come in handy:
use ORDER BY .. DESC and LIMIT 1:
SELECT
t1.Invoice_Num,
t1.Tracking_Num
FROM table1 t1
JOIN table2 t2 USING(Invoice_Num)
WHERE t2.Customer_Num = 'cust1'
ORDER BY t2.Invoice_Date DESC
LIMIT 1
SQL Fiddle

SQL DEMO
SELECT
t1.Invoice_Num,
t1.Tracking_Num
FROM table1 t1
JOIN table2 t2
ON t1.Invoice_Num = t2.Invoice_Num
JOIN ( SELECT MAX(t2b.Invoice_Date) Last_Sale
FROM table2 t2b
WHERE t2b.Customer_Num = 'cust1'
) LS
ON t2.Invoice_Date = LS.Last_Sale
Be carefull because if multiple rows share the Last Sale you will get multiple rows.

Related

Duplicate and get the last item from the mysql table

table 1 t1
+----+----------+
| id | name |
+----+----------+
| 1 | free |
| 2 | basic |
| 3 | advanced |
+----+----------+
table 2 t2
+----+-------+------+
| id | t1_fk | cost |
+----+-------+------+
| 1 | 2 | 1650 |
| 3 | 3 | 2000 |
| 4 | 2 | 550 |
+----+-------+------+
I want to get the output of t2 table but without duplicates. I was able to get this using GROUP BY function. Also i need the last item on the duplicate (i got stuck here).
Here's what i tried and it didn't work.
SELECT id cost FROM t2 GROUP BY t1_fk ORDER BY MAX(id) DESC
any help
On MySQL 8+, we can use ROW_NUMBER here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY t1_fk ORDER BY id DESC) rn
FROM t2
)
SELECT id, t1_fk, cost
FROM cte
WHERE rn = 1;
On earlier versions of MySQL, one canonical way to handle this would be to use a join to a subquery which finds the max id value for each t1_fk:
SELECT a.id, a.t1_fk, a.cost
FROM t2 a
INNER JOIN
(
SELECT t1_fk, MAX(id) AS max_id
FROM t2
GROUP BY t1_fk
) b
ON a.t1_fk = b.t1_fk AND a.id = b.max_id;

MySQL Find records in one table that has no value of a column in another table

I have table1, containing columns (simplified):
+-------------------------+
| id | user_id | username |
+----+---------+----------+
| 1 | 123 | peter |
| 2 | 234 | john |
+-------------------------+
and table 2, containing columns (simplified):
+----------------------------------+
| id | user_id | checklist_item_id |
+----+---------+-------------------+
| 1 | 123 | 110 |
| 2 | 123 | 111 |
| 3 | 123 | 112 |
| 4 | 234 | 110 |
| 5 | 234 | 112 |
+----------------------------------+
As shown above, Each entry for user_id from table1, has multiple entries for that user_id with multiple checklist_item_ids.
I am interested in returning ONLY records that does NOT have an entry in the second table for checklist_item_id = 111. The query must return only:
+---------+
| user_id |
+---------+
| 234 |
+---------+
As user with user_id 123 DO have an entry in table two with checklist_item_id of 111.
You can use subquery, for example:
SELECT *
FROM table1
WHERE user_id NOT IN
(SELECT user_id
FROM table2
WHERE checklist_item_id = 111)
use corelated subquery
select t1.* from table1 t1 where t1.user_id not in
( select user_id from table2 t2
where t2.user_id=t1.user_id
and checklist_item_id=111
)
Or use not exist which is efficient than not in
select t1.* from table1 t1 where not exists
( select 1 from table2 t2 where t2.user_id=t1.user_id and
checklist_item_id=111
)
DEMO in Fiddle
id userid itemid
4 234 110
5 234 112
In case you need only one id then it would be
select distinct t1.userid from t1 where not exists
( select 1 from t1 t2 where t2.userid=t1.userid and
itemid=111
)
output
userid
234
Demo
Simplest and efficient approach would be using LEFT JOIN, and filtering out those rows, where there is no matched record for checklist_item_id = 111
SELECT DISTINCT t1.user_id
FROM table1 AS t1
LEFT JOIN table2 AS t2 ON t2.user_id = t1.user_id AND
t2.checklist_item_id = 111
WHERE t2.user_id IS NULL

mySQL largest value by unique entry

I have tried to solve the following problem for the last couple of hours and could not find anything that pointed me in the right direction on Google or Stackoverflow. I believe that this could be a similar problem, but I did not really understand what the author wanted to achieve, hence I am trying it with my own concrete example:
I have a table that basically tracks prices of different products over time:
+------------+--------+----------+
| Product_id | Price | Time |
+------------+--------+----------+
| 1 | 1.30 | 13:00:00 |
| 1 | 1.10 | 13:30:00 |
| 1 | 1.50 | 14:00:00 |
| 1 | 1.60 | 14:30:00 |
| 2 | 2.10 | 13:00:00 |
| 2 | 2.50 | 13:30:00 |
| 2 | 1.90 | 14:00:00 |
| 2 | 2.00 | 14:30:00 |
| 3 | 1.45 | 13:00:00 |
| 3 | 1.15 | 13:30:00 |
| 3 | 1.50 | 14:00:00 |
| 3 | 1.55 | 14:30:00 |
+------------+--------+----------+
I would now like to query the table so that the rows with max. Price for each product are returned:
+------------+--------+----------+
| Product_id | Price | Time |
+------------+--------+----------+
| 1 | 1.60 | 14:30:00 |
| 2 | 2.50 | 13:30:00 |
| 3 | 1.55 | 14:30:00 |
+------------+--------+----------+
Also, in case of duplicates, i.e. if there is a max. Price at two different points in time, it should only return one row, preferably the one with the smallest value of time.
I have tried MAX() and GREATEST(), but could not achieve the desired outcome to show the wanted values for each product. Efficiency of the query is not the most important factor, but I have about 500 different products with several million rows of data, hence splitting the table by unique product did not seem like an appropriate solution.
Group the data product id and pick the max price and max time
select t1.product_id,t1.price,min(t1.time) as time from your_table t1
inner join (
select Product_id,max(price)as price from
your_table group by Product_id
) t2 on t1.Product_id=t2.Product_id and t1.price=t2.price group by t1.product_id
Sql Fiddle Example:
http://sqlfiddle.com/#!9/020c3/9
http://sqlfiddle.com/#!9/020c3/1
SELECT p.*
FROM prices p
LEFT JOIN prices p1
ON p.product_id = p1.product_id
AND p.time<p1.time
WHERE p1.product_id IS NULL
If you need maximum price to get you can:
http://sqlfiddle.com/#!9/020c3/6
SELECT p.*
FROM prices p
LEFt JOIN prices p1
ON p.product_id = p1.product_id
AND p.price<p1.price
WHERE p1.product_id IS NULL;
And the last approach since I didn't get the goal from the beggining:
http://sqlfiddle.com/#!9/ace04/2
SELECT p.*
FROM prices p
LEFt JOIN prices p1
ON p.product_id = p1.product_id
AND (
p.price<p1.price
OR (p.price=p1.price AND p.time<p1.time)
)
WHERE p1.product_id IS NULL;
This solution is assuming the existence of an additional my_table.id column that needs to be used in case there are duplicate values for (Product_id, price, time) in your table. id is assumed to be a unique value in the table.
SELECT *
FROM my_table t1
WHERE NOT EXISTS (
SELECT *
FROM my_table t2
WHERE t1.Product_id = t2.Product_id
AND ((t1.price < t2.price) OR
(t1.price = t2.price AND t1.time > t2.time) OR
(t1.price = t2.price AND t1.time = t2.time AND t1.id > t2.id))
)
Alternatively, the predicate on price and time could also be expressed using a row value expression predicate (not sure if it's more readable, as t1 and t2 columns are mixed in a each row value expression):
SELECT *
FROM my_table t1
WHERE NOT EXISTS (
SELECT *
FROM my_table t2
WHERE t1.Product_id = t2.Product_id
AND (t1.price, t2.time, t2.id) < (t2.price, t1.time, t1.id)
)

Sql return one row from right table every left table result

My question is how do I get 1 result from each result from Table1 for Table2? And not al results also from table2? I already searches stack overflow I found how to solve this with a id but I want to know how to do this with a Varchar column.
Table1
+--------------+-------------+
| Ip (Varchar) | Count (Int) |
+--------------+-------------+
| 1.1.1.1 | 9 |
| 1.1.1.2 | 6 |
| 1.1.1.3 | 1 |
+--------------+-------------+
Table2
+-------------+--------------+
| Name (Text) | Ip (Varchar) |
+-------------+--------------+
| User01 | 1.1.1.1 |
| User034 | 1.1.1.1 |
| User012 | 1.1.1.1 |
| User21 | 1.1.1.2 |
| User24 | 1.1.1.2 |
| User3 | 1.1.1.3 |
+-------------+--------------+
Wanted result
+--------+---------+-------+
| Name | Ip | Count |
+--------+---------+-------+
| User01 | 1.1.1.1 | 9 |
| User21 | 1.1.1.2 | 6 |
| User3 | 1.1.1.3 | 1 |
+--------+---------+-------+
It does not matter what name will be returned.
Because you just need a random name, you can use group by since you're using mysql:
select t1.ip, t1.count, t2.name
from table1 t1
join table2 t2 on t1.ip = t2.ip
group by t1.ip
Depending on your data, you may need an outer join as well.
SQL Fiddle Demo
You should start by joining the two tables together on the condition that the ip address matches:
SELECT t2.name, t2.ip, t1.count
FROM firstTable t1
JOIN secondTable t2 ON t1.ip = t2.ip;
However, this will return all rows in the second table with a matching ip, so results will appear more than once. If you only want to see specific pairings, you should group by the ip column:
SELECT t2.name, t2.ip, t1.count
FROM firstTable t1
JOIN secondTable t2 ON t2.ip = t1.ip
GROUP BY ip;
As far as the name goes, if you're not picky you can leave it as above and it will be picked arbitrarily, or you could chose something consistent like the minimum username:
SELECT MIN(t2.name), t2.ip, t1.count
FROM firstTable t1
JOIN secondTable t2 ON t2.ip = t1.ip
GROUP BY ip;
Here is an SQL Fiddle example. It shows all three queries so you can visualize the steps I took.

How to limit LEFT JOIN to one result AND return lowest value from the result set?

Suppose I have a query like this:
SELECT t1.name, t2.likes
FROM table_1 t1
LEFT JOIN table_2 t2 ON t1.name = t2.name
How could that left join be modified to make it do the following:
SELECT likes FROM table_2 WHERE name = [name from table 1] ORDER BY likes ASC
I tried writing a subquery, but it didn't work because (I assume) you can't use a variable from outside the subquery (e.g. the name) inside the subquery (the name is not constant, as the result set from the query will be more than one).
Is that possible to do?
EDIT: The tables would be something like this:
table_1:
+------+--------+
| id | name |
+------+--------+
| 0 | cat |
| 1 | dog |
+------+--------+
table 2:
+------+--------+---------+
| id | name | likes |
+------+--------+---------+
| 0 | cat | 23 |
| 1 | cat | 2 |
| 2 | cat | 53 |
| 3 | dog | 25 |
| 4 | dog | 12 |
+------+--------+---------+
So, what I'm wanting is:
+--------+---------+
| name | likes |
+--------+---------+
| cat | 2 |
| dog | 12 |
+--------+---------+
SELECT
t1.name, t2.likes
FROM table_1 t1
LEFT JOIN (
SELECT
name,
MIN(likes) as likes
FROM table_2
GROUP BY name
) as t2 ON t1.name = t2.name
You can do it with a subquery too but i think that will be slower:
SELECT
t1.name,
(SELECT MIN(likes) FROM table_2 WHERE table_2.name = t1.name)
FROM table_1 t1
Minimum likes per name:
SELECT name, MIN(likes) AS likes
FROM table_2
GROUP BY name
If you need more information about the name, do a join to the table_1 table, incorporating the above query into a subselect:
SELECT a.name, b.likes
FROM table_1 a
LEFT JOIN
(
SELECT name, MIN(likes) AS likes
FROM table_2
GROUP BY name
) b ON a.name = b.name