Select distinct values from two columns - mysql

I have a table with the following structure:
itemId | direction | uid | created
133 0 17 1268497139
432 1 140 1268497423
133 0 17 1268498130
133 1 17 1268501451
I need to select distinct values for two columns - itemId and direction, so the output would be like this:
itemId | direction | uid | created
432 1 140 1268497423
133 0 17 1268498130
133 1 17 1268501451
In the original table we have two rows with the itemId - 133 and direction - 0, but we need only one of this rows with the latest created time.
Thank you for any suggestions!

Use:
SELECT t.itemid,
t.direction,
t.uid,
t.created
FROM TABLE t
JOIN (SELECT a.itemid,
MAX(a.created) AS max_created
FROM TABLE a
GROUP BY a.itemid) b ON b.itemid = t.itemid
AND b.max_created = t.created
You have to use an aggregate (IE: MAX) to get the largest created value per itemid, and join that onto an unaltered copy of the table to get the values associated with the maximum created value for each itemid.

select t1.itemid, t1.direction, t1.uid, t1.created
from (select t2.itemid, t2.direction, t2.created as maxdate
from tbl t2
group by itemid, direction) x
inner join tbl t1
on t1.itemid = x.itemid
and t1.direction = x.direction
and t1.created = x.maxdate

Related

mysql LEFT JOIN followed by GROUP BY with BETWEEN Ranges

I am stumped with how I should proceed. Here is my current LEFT JOIN command which works just fine:
SELECT t1.avg_temperature as T_aver,
t2.new_confirmed as count
FROM 3_day_avg as t1
LEFT JOIN table2 as t2 on t1.date = t2.date
And this works great to make this table:
T_aver |count|
-----------------
-0.2 | 2 |
3 | 2 |
5 | 1 |
-2.3 | 4 |
22 | 0 |
But now I want to take it one step further and group by ranges of T_aver (bins like 0-5, 6-10, 11-15, etc) and SUM() the count column. For example, If I was to place the range on the LEFT JOIN table example above of -10 to 0, and 0 to 30, the final table would look like this:
Trange |count|
-----------------
-10 - 0 | 6 |
0 - 30 | 3 |
This above transformation is where I am stumped and I fear to make my life simpler I just need to create one big table instead...
Thanks in advance
You were very close! Just start with your existing query and then wrap it.
For your bins use another lookup table if you don't want to be bound to a fixed interval:
bins:
mintemp | maxtemp
-10 | 0
0 | 30
I will use num instead of count, as I never use reserved words as columns:
SELECT
CONCAT(mintemp, ' - ', maxtemp) AS Trange,
SUM(baseview.num) AS num
FROM bins
INNER JOIN (
SELECT
t1.avg_temperature as T_aver,
t2.new_confirmed as num
FROM 3_day_avg as t1
LEFT JOIN table2 as t2 on t1.date = t2.date
) AS baseview
ON baseview.T_aver>bins.mintemp AND baseview.T_aver<=bins.maxtemp
GROUP BY bins.mintemp;
SELECT ranges.caption Trange,
SUM(t2.new_confirmed) as `count`
FROM 3_day_avg as t1
LEFT JOIN table2 as t2 on t1.date = t2.date
JOIN ( SELECT -10 t_from, 0 t_to, '-10 - 0' caption
UNION ALL
SELECT 0, 30, '0 - 30' ) ranges ON t1.avg_temperature >= ranges.t_from
AND t1.avg_temperature < ranges.t_to
GROUP BY ranges.caption;
It is better to create static ranges table instead of dynamically generated. This allows to create and to store a lot of pre-defined sets of ranges.
You can group by a CASE expression which contains your bins:
SELECT
CASE
WHEN t1.avg_temperature >= -10 and t1.avg_temperature <= 0 THEN '-10 - 0'
WHEN t1.avg_temperature > 0 and t1.avg_temperature <=30 THEN '0 - 30'
END AS Trange,
SUM(t2.new_confirmed) AS count
FROM 3_day_avg AS t1 LEFT JOIN table2 AS t2
ON t1.date = t2.date
GROUP BY Trange
You may add more bins in the CASE expression, change the ranges and the inequality signs to suit your requirement.

Create a mysql function that accepts a result set as parameter?

Is it possible to create a mySQL function that accepts as a parameter the result set from a query?
Basically I have a lot of queries that will return a result result set as follows:
id | score
70 | 25
71 | 7
72 | 215
74 | 32
75 | 710
76 | 34
78 | 998
79 | 103
80 | 3
I want to normalize the values such that they come to a range between 0 and 1.
The way I thought I'd do this was by applying calculation:
nscore = (score-min(score))/(max(score) - min(score))
to get following result
id | score
70 | 0.022
71 | 0.004
72 | 0.213
74 | 0.029
75 | 0.710
76 | 0.031
78 | 1.000
79 | 0.100
80 | 0.000
But I'm not able to come up with a query to get the min and max in this query along with results, hence thought of using a function (cannot use stored procedure) but couldn't documentation on how to pass a result set.
Any help appreciated!Thanks!
EDIT:
The score field in result is a computed field. Cannot select it directly.
For eg: Sample query that returns the above result -
select t.id as id, count(*) as score
from tbl t
inner join tbl2 t2 on t.idx = t2.idx
where t2.role in (.....)
just for demo purpose, not actual schema or query
No. MySQL doesn't support defining a function with a resultset as an argument.
Unfortunately, MySQL does not support Common Table Expression (CTE), and does not support Analytic functions.
To get this result from a MySQL query... one way to do that in MySQL would require the original query to be returned as an inline view, two times ...
As an example:
SELECT t.id
, (t.score-s.min_score)/(s.max_score-s.min_score) AS normalized_score
FROM (
-- original query here
SELECT id, score FROM ...
) t
CROSS
JOIN ( SELECT MIN(r.score) AS min_score
, MAX(r.score) AS max_score
FROM (
-- original query here
SELECT id, score FROM ...
) r
) s
ORDER BY t.id
EDIT
Based on the query added to the question ...
SELECT q.id
, (q.score-s.min_score)/(s.max_score-s.min_score) AS normalized_score
FROM ( -- original query goes here
-- ------------------------
select t.id as id, count(*) as score
from tbl t
inner join tbl2 t2 on t.idx = t2.idx
where t2.role in (.....)
-- ------------------------
) q
CROSS
JOIN ( SELECT MIN(r.score) AS min_score
, MAX(r.score) AS max_score
FROM ( -- original query goes here
-- ------------------------
select t.id as id, count(*) as score
from tbl t
inner join tbl2 t2 on t.idx = t2.idx
where t2.role in (.....)
-- ------------------------
) r
) s
ORDER BY q.id

MySQL includes a specific row with order by

Given 2 tables, I want to generate top 3 highest amount from [Purchase] table.
Additional criteria is [Crocs] must be included in top 3 of the records.
I have following SQL, but it cannot generates the result as I wanted (Result A), please guide me on how to pull out the result in Result B. Thank you.
Table (Purchase):
Purchase_ID | StoreID | Amount
------------|---------|--------
1 | 21 | 22
2 | 23 | 13
3 | 25 | 6
4 | 26 | 23
5 | 28 | 18
Table (Store):
Store_ID | StoreName
---------|----------
21 | Adidas
22 | Nike
23 | Puma
24 | New Balance
25 | Crocs
26 | Converse
SQL:
SELECT IF(SUM(amount) IS NULL, 0, SUM(amount)) as totalAmount
FROM (
SELECT a.amount
FROM purchase a
INNER JOIN store b
ON a.store_id = b.storeid
GROUP BY a.amount
HAVING b.StoreName = 'Crocs'
ORDER BY a.amount DESC
LIMIT 3
) t
Result A: $6
Explanation A: Amount of Crocs is $6
Result B: $51
Explanation B: Total Amount of top 3 = $22 (Adidas) + 23 (Puma) + $6 (Crocs)
The answer from scaisEdge is almost right, but the first query could also return a row with crocs and the sorting is wrong (order by max(a.amount) limit 2 means that the lowest 2 results will be shown). Additionally you could wrap the query in another select query to sort the results
SELECT * FROM (
SELECT b.storename, max(a.amount) as maxAmount
FROM purchase a
INNER JOIN store b ON a.store_id = b.storeid
WHERE b.storename != 'crocks'
GROUP BY a.storename
ORDER BY max(a.amount) DESC
LIMIT 2
UNION
SELECT b.storename, a.amount as maxAmount
FROM purchase a
INNER JOIN store b
ON a.store_id = b.storeid
WHERE b.storename='crocks'
ORDER BY a.amount DESC
LIMIT 1
) ORDER BY maxAmount DESC
You could use an union
SELECT b.storename, max(a.amount)
FROM purchase a
INNER JOIN store b
ON a.store_id = b.storeid
GROUP BY a.storename
order by max(a.amount) limit 2
union
SELECT b.storename, a.amount
FROM purchase a
INNER JOIN store b
ON a.store_id = b.storeid
where b.storename='crocks'
try this one:
SELECT sum(amount)as sum_amount,a.store_id,storename,category from
(select amount,store_id from tbl_purchase) as a
inner JOIN
(select store_id,storename,category from tbl_store)as b on a.store_id = b.store_id where b.category = 'supermarket' GROUP BY category

MySQL select upto first occurrence of condition matching

Id | Price
----------------
1 | 10
2 | 20
3 | 40
4 | 10
I need to select ids where first occurrence of summation of price is greater than or equal 55 matching from the bottom. At this case --
I will have 4,3,2 ids selected.
Well, this is kinda tricky for MySQL since it doesn't support any window fuctions and becuase you want to include the first occurrence as well. You can try this:
SELECT * FROM (
SELECT t.id,
(SELECT sum(s.price) FROM YourTable s
WHERE s.id <= t.id) as cuml_sum
FROM YourTable t) ss
WHERE ss.cuml_sum < 55
--Will select all the record will the sum < 55
UNION ALL
SELECT * FROM (
SELECT t.id,
(SELECT sum(s.price) FROM YourTable s
WHERE s.id <= t.id) as cuml_sum
FROM YourTable t) tt
WHERE tt.cuml_sum >= 55
ORDER BY tt.cuml_sum
LIMIT 1
--Will select the first record that have sum >= 55

Column calculated by column with grouping

I have a simple table -
id | date | type | value
-------------------------
1 1/1/14 A 1
2 1/1/14 A 10
3 2/1/14 A 10
4 2/1/14 A 15
5 2/1/14 B 15
6 2/1/14 B 20
I would like to create a new column which calculates the minimum value per day per type. So giving the following results -
id | date | type | value | min_day
-----------------------------------
1 1/1/14 A 1 1
2 1/1/14 A 10 1
3 2/1/14 A 10 10
4 2/1/14 A 15 10
5 2/1/14 B 15 15
6 2/1/14 B 20 15
Is this possible? If so how would I go about it? I've been looking into triggers.
Thanks for any help.
First create a field named min_day in your table. Then you can use JOIN in an UPDATE query.
Try this:
UPDATE TableName T1 JOIN
(SELECT date,type,MIN(value) as MinValue
FROM TableName
GROUP BY type,date) T2 ON T1.type=T2.type AND T1.date=T2.date
SET T1.min_day = T2.MinValue
An example in SQL Fiddle.
EDIT:
For day-wise grouping:
UPDATE TableName T1 JOIN
(SELECT MONTH(date) as mon,type,MIN(value) as MinValue
FROM TableName
GROUP BY type,MONTH(date)) T2 ON T1.type=T2.type AND MONTH(T1.date)=T2.mon
SET T1.min_day = T2.MinValue
Result in SQL Fiddle.
Assuming that your table's name is mytable, try this:
SELECT mt.id,
mt.date,
mt.type,
mt.value,
mt.min_day,
md.min_value
FROM mytable mt
LEFT JOIN
(SELECT date, MIN(value) min_value FROM mytable GROUP BY DATE
) md
ON mt.date=md.date;
SELECT t1.*,
t2.min_day
FROM Table1 AS t1
JOIN
(SELECT date,TYPE,
min(value) AS min_day
FROM table1
GROUP BY date,TYPE) AS t2 ON t1.TYPE = t2.TYPE
AND t1.date = t2.date