Select multiple rows with max date - mysql

I'm new to mysql so please help me out with this.
I have a table containing the following columns:
nr | date | hour | user | shop | brand | categ | product | price | promo
183 02/03/14 17:06 cristi 186 brand1 categ 1 prod 1 299 no
184 02/03/14 17:06 cristi 186 brand2 categ 2 prod 2 399 yes
184 01/03/14 17:06 cristi 186 brand3 categ 3 prod 3 199 no
The query that I use is
SELECT *
FROM evaluari
WHERE magazin = %s HAVING MAX(data)
Where "s" is the shop ID (186).
but that return only the first row containing 02/03/14 date. How can I show both/all rows containing the same max date?

Try not to name colums with reserved words like "date" it might cause you problems.
You can do what you want like this:
SELECT * FROM evaluari WHERE magazin = 186 AND date = (SELECT MAX(date) from evaluari WHERE magazin = 186)

Probably, not optimal, but at first swing, you could do this
SELECT * FROM evaluari
where date IN (SELECT date FROM evaluari WHERE magazin = %s HAVING MAX(date))
AND magazin = %s;
In fact, this really rubs me as nasty... going to try to figure something smoother. Stay tuned :)

Related

How to assign to each row a number of times a value appears in the whole table?

I'm trying to run an SQL query on Vertica but I can't find a way to get the results I need.
Let's say I have a table showing:
productID
campaignID (ID of the sales campaign)
calendarYearWeek (calendar week when the campaign was active [usually they're active for 5 days)
countryOrigin (in which country was the product sold, as it's international sales)
valueLocal (price in local currency)
What I need to do is to find products sold in different countries and compare their prices between markets.
Sometimes the campaigns are available only in one country, sometimes in more, so to avoid having hundreds of thousands of unnecessary rows that I can't compare to others, I want to distill only those products that were available in more than 1 countryOrigin.
What's important - a product can be available in different campaigns with a different price.
That's why in my SELECT statement I added a new column:
calendarYearWeek||productID||campaignID AS uniqueItem - that way I know that I'm checking the price only for a specific product in a specific campaign during a specific week of year.
The table is also joined with another table to get exchange rates etc., so it's also GROUPed BY, so in each row I have a price and average exchange rate for a given uniqueItem in a specific country.
If I run this query, it works but even just for this year it gives me several million results, most of which I don't need because these are products sold only in one country and I need to compare prices across different markets.
So what I thought I need is to assign to each row a number of times a uniqueItem value appears in the whole table. If it's 1 - then the product is sold only in one country and I don't have to care about it. If it's 2 or 3 - this is what I need. Then I can filter out the unnecessary results in the WHERE clause ( > 1) and I can work on a smaller, better data set.
I tried different combinations of COUNT, I tried row_number + OVER(PARTITION BY) (works only partially, as when a product is available in 2 or more countries it counts the rows, but still I cannot filter out "1" because then I'll lose the "first" country on the list). I thought about MATCH_RECOGNIZED, but I've never used it before and I think it's not available in Vertica.
Sorry if it's messy, but I'm not really advanced in SQL and English is not my native language.
Do you have any ideas how to get only the data I need?
What I have now is:
SELECT
a.originCountry,
a.calendarYearWeek,
a.productID,
a.campaignId,
a.valueLocal,
ROUND(AVG(b.exchange_rate),4),
a.calendarYearWeek||a.productID||a.campaignID AS uniqueItem
FROM table1 a
LEFT JOIN table2 b
ON a.reportDate = b.reportDate
AND a.originCountry = b.originCountry
WHERE a.originCountry IN ('ES', 'DE', 'FR')
GROUP BY 3, 4, 7, 1, 5, 2
ORDER BY 3, 4, 1
----------
I need some sample data - so I make up a few rows.
You need to find the identifying grouping columns of those combinations that occur more than once in a sub select or a common table expression, to join with table1.
You need to formulate the average as an OLAP function if you want the country back in the report.
WITH
-- input, don't use in final query ..
table1(originCountry,calendarYearWeek,productID,campaignId,valuelocal,reportDate) AS (
SELECT 'ES',202203,43,142,100.50, DATE '2022-01-19'
UNION ALL SELECT 'DE',202203,43,142,135.00, DATE '2022-01-19'
UNION ALL SELECT 'FR',202203,43,142, 98.75, DATE '2022-01-19'
UNION ALL SELECT 'ES',202203,44,147,198.75, DATE '2022-01-19'
UNION ALL SELECT 'DE',202203,44,147,205.00, DATE '2022-01-19'
UNION ALL SELECT 'FR',202203,44,147,198.75, DATE '2022-01-19'
UNION ALL SELECT 'es',202203,49,150, 1.25, DATE '2022-01-19'
)
,
table2(originCountry,reportDate,exchange_rate) AS (
SELECT 'ES',DATE '2022-01-19', 1
UNION ALL SELECT 'DE',DATE '2022-01-19', 1
UNION ALL SELECT 'FR',DATE '2022-01-19', 1
)
-- end of input; real query starts here, replace following comma with "WITH" ..
,
-- you need the unique ident grouping values to join with ..
selgrp AS (
SELECT
a.calendarYearWeek
, a.productID
, a.campaignId
FROM table1 a
GROUP BY
a.calendarYearWeek
, a.productID
, a.campaignId
HAVING COUNT(*) > 1
-- chk calendarYearWeek | productID | campaignId
-- chk ------------------+--------+--------
-- chk 202203 | 43 | 142
-- chk 202203 | 44 | 147
)
SELECT
a.originCountry
, a.calendarYearWeek
, a.productID
, a.campaignId
, a.valueLocal
, AVG(b.exchange_rate) OVER w::NUMERIC(9,4) AS avg_exch_rate
-- a.calendarYearWeek||a.productID||a.campaignID AS uniqueItem
FROM table1 a
JOIN selgrp USING(calendarYearWeek,productID,campaignId)
LEFT JOIN table2 b
ON a.reportDate = b.reportDate
AND a.originCountry = b.originCountry
WHERE UPPER(a.originCountry) IN ('ES', 'DE', 'FR')
WINDOW w AS (PARTITION BY a.calendarYearWeek,a.productID,a.campaignID)
ORDER BY 3, 4, 1
-- out originCountry | calendarYearWeek | productID | campaignId | valueLocal | avg_exch_rate
-- out ---------------+------------------+-----------+------------+------------+---------------
-- out DE | 202203 | 43 | 142 | 135.00 | 1.0000
-- out ES | 202203 | 43 | 142 | 100.50 | 1.0000
-- out FR | 202203 | 43 | 142 | 98.75 | 1.0000
-- out DE | 202203 | 44 | 147 | 205.00 | 1.0000
-- out ES | 202203 | 44 | 147 | 198.75 | 1.0000
-- out FR | 202203 | 44 | 147 | 198.75 | 1.0000

Get largest values from multiple columns from latest timestamps in MySql

I'm trying to get a list of the*usedpc values across multiple similar columns, and order desc to get worst offenders. Also, I need to only select the values from the most recent timestamp for each sys_id.
Example data:
Sys_id | timestamp | disk0_usedpc | disk1_usedpc | disk2_usedpc
---
1 | 2016-05-06 15:24:10 | 75 | 45 | 35
1 | 2016-04-06 15:24:10 | 70 | 40 | 30
2 | 2016-05-06 15:24:10 | 23 | 28 | 32
3 | 2016-05-06 15:24:10 | 50 | 51 | 55
Desired result (assuming limit 2 for example):
1 | 2016-05-06 15:24:10 | disk0_usedpc | 75
3 | 2016-05-06 15:24:10 | disk2_usedpc | 55
I know I can get the max from each column using greatest, max and group timestamp to get only the latest values, but I can't figure out how to get the whole ordered list (not just max/greatest from each column, but the "5 highest values across all 3 disk columns").
EDIT: I set up a SQLFiddle page:
http://sqlfiddle.com/#!9/82202/1/0
EDIT2: I'm very sorry about the delay. I was able to get all three solutions to work, thank you. If #PetSerAl can put his solution in an answer, I'll mark it as accepted, as this solution allowed me to very smoothly customise further.
You can join vm_disk table with three row table to create separate row for each of yours disks. Then, as you have row per disk now, you can easily filter or sort them.
select
`sys_id`,
`timestamp`,
concat('disk', `disk`, '_usedpc') as `name`,
case `disk`
when 0 then `disk0_usedpc`
when 1 then `disk1_usedpc`
when 2 then `disk2_usedpc`
end as `usedpc`
from
`vm_disk` join
(
select 0 as `disk`
union all
select 1
union all
select 2
) as `t`
where
(`sys_id`, `timestamp`) in (
select
`sys_id`,
max(`timestamp`)
from `vm_disk`
group by `sys_id`
)
order by `usedpc` desc
limit 5
Maybe something like this would work... I know it may look pretty redundant but it could save overhead caused by doing multiple joins to the same table:
SELECT md.Sys_id,
md.timestamp,
CASE
WHEN
md.disk0_usedpc > md.disk1_usedpc
AND
md.disk0_usedpc > md.disk2_usedpc
THEN 'disk0_usedpc'
WHEN
md.disk1_usedpc > md.disk0_usedpc
AND
md.disk1_usedpc > md.disk2_usedpc
THEN 'disk1_usedpc'
ELSE 'disk2_usedpc'
END AS pcname,
CASE
WHEN
md.disk0_usedpc > md.disk1_usedpc
AND
md.disk0_usedpc > md.disk2_usedpc
THEN md.disk0_usedpc
WHEN
md.disk1_usedpc > md.disk0_usedpc
AND
md.disk1_usedpc > md.disk2_usedpc
THEN md.disk1_usedpc
ELSE md.disk2_usedpc
END AS pcusage
FROM mydatabase md
GROUP BY md.Sys_id HAVING MAX(md.timestamp)
ORDER BY pcusage DESC
Try this:
select
t1.sys_id, t1.`timestamp`,
case locate(greatest(disk0_usedpc ,disk1_usedpc ,disk2_usedpc), concat_ws(',' ,disk0_usedpc ,disk1_usedpc ,disk2_usedpc))
when 1 then 'disk0_usedpc'
when 1 + length(concat(disk0_usedpc, ',')) then 'disk1_usedpc'
when 1 + length(concat(disk0_usedpc, ',', disk1_usedpc, ',')) then 'disk2_usedpc'
end as usedpc,
greatest(disk0_usedpc ,disk1_usedpc ,disk2_usedpc) as amount
from yourtable t1
join (
select max(`timestamp`) as `timestamp`, sys_id
from yourtable
group by sys_id
) t2 on t1.sys_id = t2.sys_id and t1.`timestamp` = t2.`timestamp`
order by t1.`timestamp` desc
-- limit 2
SQLFiddle Demo
How it works, the sub query here is try to get the latest row for each group sys_id, as one kind of way in many solutions. Then you should get the greatest column in disk0_usedpc ,disk1_usedpc ,disk2_usedpc, as you wrote in your question, the function greatest is the plan. So greatest(disk0_usedpc ,disk1_usedpc ,disk2_usedpc) as amount can help you get the amount.
But also you want that column's name, here I used locate and concat, concat_ws(which avoids writing so many separators, here is comma ,).
Let's take row 1 | 2016-05-06 15:24:10 | 75 | 45 | 35 as an example:
concat_ws(',' ,disk0_usedpc ,disk1_usedpc ,disk2_usedpc) will give us "75,45,35", here 75's index in this string is 1, 45 is 4, 35 is 7.
As you see, locate(greatest(disk0_usedpc ,disk1_usedpc ,disk2_usedpc), concat_ws(',' ,disk0_usedpc ,disk1_usedpc ,disk2_usedpc)) will return 1, so the greatest row is disk0_usedpc, here it makes.

Sum gave me wrong amount

This is my table:
(Stuff Table)
id_stuff | prody_type | semester | amount_prod
1 090 1 10
2 210 2 35
(Amount Table)
id_amount | prod_type | semester | amount_stuff
1 090 1 12
2 210 2 15
(Product Table)
id_prod | type_prod | prod_number
1 090 010
2 210 020
And here's my code from my model file:
$this->db->select("sum(amount_stuff) as 'tot_amount_stuff'")
->from('stuff')
->join('product','prod_type=type_prod')
->join('amount','stuff.prod_type=amount.prod_type')
->WHERE('amount.semester', 2);
Then code to show in my page:
for each.......
<td><?php echo $row->tot_amount_stuf; ?></td>
And in my html page shows: 50 but that's wrong, the correct amount is 15
It's seems the 'sum' taking all from the tables 'Stuff' and 'Amount' on semester 2, but I only need to take/show the sum of amount_stuff from the Amount Table not from both tables.
I'm little bit confused here...Hope anyone can help me.
Best Regards,
Try
SELECT sum(amount_stuff) as 'tot_amount_stuff'
FROM Amount
JOIN Stuff ON (Stuff.prody_type = Amount.prody_type)
JOIN product ON (Amount.prody_type = Product.type_prod)
WHERE amount.semester = 2;
Make sure you're referencing tables and columns in your select
$this->db->select("sum(amount.amount_stuff) as 'tot_amount_stuff'")
->from('stuff')
->join('product','prod_type=type_prod')
->join('amount','stuff.prod_type=amount.prod_type')
->WHERE('amount.semester', 2);
You'll want your SQL to render like so:
SELECT sum(amount.amount_stuff) as 'tot_amount_stuff'
FROM stuff
JOIN product ON (amount.prod_type = product.type_prod)
JOIN amount ON (stuff.prod_type = amount.prod_type)
WHERE amount.semester = '2'
You could also use aliases to shorten this process
SELECT sum(a.amount_stuff) as 'tot_amount_stuff'
FROM stuff s
JOIN product p ON (a.prod_type = p.type_prod)
JOIN amount a ON (s.prod_type = a.prod_type)
WHERE a.semester = '2'

SQL SUM + Distinct

I want to know the request with which I displayed the sum of the amounts of the various clients that do not repeat with the SUM function and DISTINCT.
I used :
SELECT DISTINCT id_721z, SUM(montant) AS somme_montant
FROM `roue_ventes_cb`
WHERE `date_transaction` between '2015/01/01' and '2015/01/21';
But the result is not displayed correctly. I have this data:
id_721z | montant
1 | 15
1 | 15
2 | 22
2 | 22
2 | 22
I would like to show total_montant = 37 but not
id_721z | montant
1 | 30
2 | 66
SELECT SUM(montant) AS somme_montant
FROM (
SELECT DISTINCT id_721z, montant
FROM `roue_ventes_cb`
WHERE `date_transaction` between '2015/01/01' and '2015/01/21'
) AS t
This will sum all different montants. But if two ids have the same montant it will only count it once.
SELECT id_721z, SUM(DISTINCT montant) AS somme_montant
FROM `roue_ventes_cb`
WHERE `date_transaction` between '2015/01/01' and '2015/01/21';
So I will prefer emiros answer in any case. It is safer and distint will have a performance penalty anyway.

MySQL returning rows which are closest in value to a specific number

I have a table in my database similar to the one below:
id | name | score
==========================
... ... ...
44 Bob 89
45 Jane 567
46 Andrew 22
... ... ...
score will always be a positive integer. Let's say I want to return the 5 users who have scores which are closest to Jane. How would I do this using SQL (or Eloquent), assuming 5 is a variable?
select * from your_table
order by abs(score - (select score from your_table where name = 'Jane'))
limit 5