MySQL update with conditions based on another tables value - mysql

I have two tables in MySQL. I would like to set the value of the 'Total' column to be equal to the Ratio x (Table A column if the item begins with A) or Ratio x (Table B column if the item begins with B) and the Month column in both tables match.
create table zeus(id int primary key,
Month varchar(10),
Items varchar(20),
Ratio decimal(3,2),
Total decimal(10,2));
create table ares(id int primary key,
Month varchar(10),
`Value A` decimal(10,2),
`Value B` decimal(10,2));
id
Month
Items
Ratio
Total
1
May
Bag
0.50
2
May
Apple
0.3
3
May
Bottle
0.25
4
May
Acorn
0.1
5
May
Alarm Clock
0.6
6
May
Bottle
0.25
id
Month
Total A
Total B
1
May
600
800
2
June
780
400
I have tried this
update zeus
set Total = (CASE
WHEN Items like ('A%') and Month = 'May'
THEN Ratio * (select(ares.`Value A`)
WHERE Month = 'May')
WHEN Items like ('B%') and Month = 'May'
THEN Ratio * (select(ares.`Value B`)
WHERE Month = 'May')
END);
But I can't seem to figure it out.

It looks like you just want:
update zeus
inner join ares using (Year,Month)
set zeus.Total = case
when zeus.Items like 'A%' then ares.`Value A` * zeus.Ratio
when zeus.Items like 'B%' then ares.`Value B` * zeus.Ratio
end
That will set the Total to null if Items starts with neither A nor B; add an else clause if you want it set to blank or left as the existing zeus.Total value.
Note that having arrays of column names for related values rather than a separate table with one value per row is usually considered bad database design.

Related

SQL join each row in a table with a one row from another table

The Problem
I have a table window with start and end timestamps. I have another table activity that has a timestamp. I would like to create a query that:
For each row in activity it joins with a single row from window, where the timestamp occurs between start and end, choosing the older window.
Window Table
Start
End
ISBN
0
10
"ABC"
5
15
"ABC"
20
30
"ABC"
25
35
"ABC"
Activity Table
Timestamp
ISBN
7.5
"ABC"
27.5
"ABC"
Desired Result
Start
End
ISBN
Timestamp
0
10
"ABC"
7.5
20
30
"ABC"
27.5
The Attempt
My attempt at solving this so far has ended with the following query:
SELECT
*
FROM
test.activity AS a
JOIN test.`window` AS w ON w.isbn = (
SELECT
w1.isbn
FROM
test.window as w1
WHERE a.`timestamp` BETWEEN w1.`start` AND w1.`end`
ORDER BY w1.`start`
LIMIT 1
)
The output of this query is 8 rows.
When there is guaranteed to be a single oldest window (i.e. no two Start times are the same for any ISBN)
with activity_window as (
select
a.`Timestamp`,
a.`ISBN`,
w.`Start`,
w.`End`,
row_number() over (partition by a.`ISBN`, a.`Timestamp` order by w.`Start`) rn
from
`Activity` a
inner join `Window` w on a.`ISBN` = w.`ISBN` and a.`Timestamp` between w.`Start` and w.`End`
)
select `Start`, `End`, `ISBN`, `Timestamp` from activity_window where rn = 1;
Result:
Start
End
ISBN
Timestamp
0
10
ABC
7.5
20
30
ABC
27.5
(see complete example at DB<>Fiddle)
CTEs are available from MySQL 8.0. Use subqueries when you are still on MySQL 5. Try to avoid table- and column names that are reserved words in SQL (things like Window, Start, End or Timestamp are examples for bad name choices).
Keeping an index over (ISBN, Start, End) on Window (or clustering the entire table that way by defining those three columns as the primary key) helps this query.

How can I do a table join where the composite key is an access number and a date +/- 1 day

I am trying to do a table join where the composite key is an access number and a snapshot date, and the data of interest is Status. Here's what the simple join looks like:
SELECT A.Access_Num, B.Access_Num, A.SNAPSHOT_DATE, B.SNAPSHOT_DATE, B.Status
FROM A
INNER JOIN B
ON A.Access_Num = B.Access_Numb AND A.SNAPSHOT_DATE = B.SNAPSHOT_DATE
My obstacle is that the data of interest Status is not populated on the exact match for SNAPSHOT_DATE (20% of the time). Sometimes it varies by +/- 1 from the SNAPSHOT_DATE in table A. There's still a record match, but table B will just return NA.
I am trying to write a join case that examines if Status returns as NA, to look at a composite key from the previous day for Table_B, and a composite key from the next day to see if there's a valid value available (i.e., not NA).
In the table below, I would want the query to recognize that row 3 returns an NA, so then it looks at the previous or next day to find a valid value. It should pick up that row 4 has a non-NA value, and return that one.
Row A.Access_Num B.Access_Num A.SNAPSHOT_DATE B.SNAPSHOT_DATE B.STATUS
1 11 11 12-12-2018 12-12-2018 Y
2 11 11 12-13-2018 12-13-2018 Y
3 13 13 12-05-2018 12-05-2018 NA
4 13 13 12-06-2018 Y
You can use inequalities in the ON clause:
SELECT A.Access_Num, B.Access_Num, A.SNAPSHOT_DATE, B.SNAPSHOT_DATE, B.Status
FROM A INNER JOIN
B
ON A.Access_Num = B.Access_Numb AND
A.SNAPSHOT_DATE >= B.SNAPSHOT_DATE - INTERVAL 1 DAY AND
A.SNAPSHOT_DATE <= B.SNAPSHOT_DATE + INTERVAL 1 DAY ;

Mysql search text in one column

For Example our table;
İd--------Price---------Level
1 ------100-300 ------ 1,2
2 ---------200----------1
3 ------100-280--------1,3
We want search a price value is 110. 110 is between 100-300 and 100-280 so id 1 and id 2 must listed. Can we write this query with my-sql?.
Additional , we want search price and level value. Price 110 and level 2 searching. Can we write this query with my-sql?.
Thank You
Remember that database tables should be created with the idea that it will satisfy your query needs. It doesn't make sense to have a table with a price "100-300" which represents a String (or in mysql a VARCHAR) and you want to treat this as a number. So what to do?
1) The first thing i would do is re write my table schema having a minPrice and maxPrice fields, so this way you could have this:
İd----minPrice---maxPrice------level
1 ------100---------300 ------ 1,2
2 ------200---------200----------1
3 ------100---------280--------1,3
2) Then your query would be like:
SELECT id FROM Mytable x WHERE myValue >= x.minPrice AND myValue <= x.maxPrice
3) In case you also want to look for a level value. you would do:
SELECT id FROM Mytable x WHERE myValue >= x.minPrice AND myValue <= x.maxPrice AND myLevelValue IN (x.level)
Use two columns for the price ...
ID FROM TO LEVEL
1 100 300 1,2
2 200 200 1
3 100 280 1,3
Then SQL:
SELECT `level` FROM `table`
WHERE X >= `from` AND X <= `to`

Update access column from existing values

I have a MS access table with the following columns and sample records.
How do I update the adDate value with the least LastSaleDate for each ProductID.
orderID productID lastsaleDate adDate
1 1 10/20/2012
2 1 5/10/2007
3 1 4/1/2004
4 1 20/11/2011
5 2 10/10/2010
6 2 12/10/1972
For example the adDate for ProductID 1 will be 4/1/2004
and for ProductID 2 will be 12/10/1972
You can use DMin:
UPDATE sales
SET sales.adDate = DMin("lastsaleDate","sales","productID=" & productid)
Unless you have a compelling reason to actually store adDate values in your table, consider simply computing adDate with a query any time you need it. That way you don't risk ever displaying adDate values which have not been updated to reflect the latest changes to the underlying data.
SELECT
y.orderID,
y.productID,
y.lastsaleDate,
sub.adDate
FROM
YourTable AS y
INNER JOIN
(
SELECT productID, Min(lastsaleDate) AS adDate
FROM YourTable
GROUP BY productID
) AS sub
ON y.productID = sub.productID;

using levenshtein distance ratio to compare 2 records

I've created the mysql user function using the levenshtein distance and ratio source codes. I am comparing 2 records and based on a 75% match I want to select the record.
Order comes into table paypal_ipn_orders with an ITEM title
A query executes against a table itemkey to find a 75% match in a record called ITEM as well
if a 75% title is match it assigns an eight digit number from table itemkey to table paypal_ipn_orders
Here is the query
UPDATE paypal_ipn_orders
SET sort_num = (SELECT sort_id
FROM itemkey
WHERE levenshtein_ratio(itemkey.item, paypal_ipn_orders.item_name) > 75)
WHERE packing_slip_printed = 0
AND LOWER(payment_status) = 'completed'
AND address_name <> ''
AND shipping < 100
I have adjusted this a few times but it's failing between line 4 and 5 at the levenshtein_ratio part. If it works it says that the subquery returns more than one row. I don't know how to fix it to make it return the correct result, I just lost as to how to make this work.
A subquery on a SET should only return one value. If itemkey has more than one item that is 75% of item_name what do you want to do? The below will use one of the best matches:
UPDATE paypal_ipn_orders
SET sort_num = (SELECT sort_id
FROM itemkey
WHERE levenshtein_ratio(itemkey.item, paypal_ipn_orders.item_name) > 75
ORDER BY levenshtein_ratio(itemkey.item, paypal_ipn_orders.item_name) DESC
LIMIT 1)
WHERE packing_slip_printed = 0
AND LOWER(payment_status) = 'completed'
AND address_name <> ''
AND shipping < 100