SQL to update selected fields in table from view - mysql

I am new to this, so I apologize upfront for any confusion/frustration. I appreciate any help that I can get!
I have a table (MainTable) that I have created two views with (GoodTable and BadTable).
Each table has 4 columns (ID, UserID, key, value).
ID is the Primary Key, but
UserID can repeat in several rows.
What I need to do in the Main table is find the IDs that are in the BAD table, and update the values from the value column of the GOOD table, based on a match of UserID AND a LIKE match with the key column, into the MAIN table.
I hope that makes sense.
I've tried:
UPDATE MainTable
SET value = (SELECT value FROM GoodTable
WHERE MainTable.UserID = GoodTable.UserID
AND MainTable.key LIKE "some%key%specifics");
This gets me ALMOST there, but the problem is if it doesn't find the LIKE key specifics, it returns a NULL value and I want it to keep it's original value if it's not in BadTable (BadTable is essentially all of the keys that match the LIKE key specifics). Obviously the above doesn't use BadTable, but I thought that might help me solve this (not the case, so far!)...
Here's a bit of an example:
MainTable:
ID UserID key value
1 1 key1 good value
2 1 key2 bad value
3 1 key3 unrelated value
4 2 key1 good value
5 2 key2 bad value
6 2 key3 unrelated value
GoodTable:
ID UserID key value
1 1 key1 good value
4 2 key1 good value
BadTable:
ID UserID key value
2 1 key2 bad value
5 2 key2 bad value
What I want MainTable to change to:
ID UserID key value
1 1 key1 good value
2 1 key2 good value
3 1 key3 unrelated value
4 2 key1 good value
5 2 key2 good value
6 2 key3 unrelated value
I also thought if there was something like a VLOOKUP (like in Excel) where I could say what to do if false, but I haven't been able to work that out either. I've tried some other things from researching other questions but I've spun myself dizzy now and decided to reach out for help :)
Lastly, I'm not sure if this matters or not, but this if for MySQL...
I'm sure I'm making this more complicated for myself than I need to, so I really appreciate any help anyone can provide!
UPDATE: per #Rabbit suggestion, this is the best I could come up with using the inner join (though I thought this would add to the MainTable, but I want to keep the number of rows in MainTable the same, just update that one field for the applicable rows..):
UPDATE MainTable
JOIN GoodTable ON MainTable.ID = GoodTable.ID
SET value = (SELECT value FROM GoodTable
WHERE MainTable.UserID = GoodTable.UserID
AND MainTable.key LIKE "some%key%specifics");
I'm sure this is an awful attempt but I am certainly a novice here!
I did manage to come up with a solution (though I am sure it is highly inefficient) -- please see answer below! (Thank you #DBug and #Rabbit for pointing me in the right direction!)

You can use the Coalesce function, which returns the first non-null argument, giving it the value column from both tables, e.g.
UPDATE MainTable
SET value = (SELECT COALESCE(GoodTable.value, MainTable.value) FROM GoodTable
WHERE MainTable.UserID = GoodTable.UserID
AND MainTable.key LIKE "some%key%specifics");
It will return GoodTable.value if it is not NULL, or MainTable.value if it is.

You've said "Main table is find the IDs that are in the BAD table, and update the values from the value column of the GOOD table, based on a match of UserID AND a LIKE match with the key column, into the MAIN table." so your sample result is wrong...
We will update the 2 and 5 ID from MainTable right? because it is in BadTable BUT 2 and 5 key is Key 2 and there's no Key 2 on GoodTable.
If I will base on your answer. This might help you. Please check
UPDATE MainTable m
INNER JOIN BadTable b ON m.id = b.id
LEFT JOIN GoodTable g ON b.UserID = g.UserID
SET m.value = g.value
A little fix on that. for the key you wanted

No need to use a subquery, you can use a join in the UPDATE clause.
UPDATE Table1
INNER JOIN Table2 ON Table1.FieldName = Table2.FieldName
SET ....
WHERE ....

This ended up working for me, but note I needed to create yet another view (MatchTable):
UPDATE MainTable
SET value = COALESCE((SELECT value FROM MatchTable
WHERE MainTable.ID = MatchTable.ID
AND MainTable.key LIKE "some%key%specifics"),(SELECT value));
MatchTable is essentially taking the BAD rows (from BadTable) and updating their value with the GOOD values from GoodTable. Here is the code I used for that:
CREATE VIEW MatchTable AS
SELECT (BadTable.ID) AS "ID", (BadTable.UserID) AS "UserID", (BadTable.key) AS "key", (SELECT value FROM GoodTable
WHERE BadTable.UserID = GoodTable.UserID) AS "value"
FROM BadTable
LEFT JOIN GoodTable
ON GoodTable.ID = BadTable.ID;
This resulted in the following data in MatchTable:
MainTable:
ID UserID key value
2 1 key2 good value
5 2 key2 good value
For completeness of the solution, and if it happens to help anyone else, here are the queries that I used to create GoodTable and BadTable, respectively:
CREATE VIEW GoodTable AS
SELECT * FROM MainTable
WHERE key = "specific_key_term";
CREATE VIEW BadTable AS
SELECT * FROM MainTable
WHERE key LIKE "some%key%specifics";
Okay, to summarize, here is the entire process that I used to get the desired results in MainTable (as notated in the posted question):
Create GoodTable view
Create BadTable view
Create MatchTable view
Run update query to update MainTable
Celebrate wildly! (Just kidding - I can't help the nagging feeling that there has to be a better/more efficient way of doing this!) But seriously, celebrate wildly!
(Thank you again to #DBug for getting me on the right track for the update portion and to #Rabbit for getting me to the MatchTable!)

Related

UPDATE with select query adding NULL rows

I am trying to fill in fields in a table with date of another table.
In the table 'blanko' I have a column 'product_sku' and 'virtuemart_product_id'.
In the table 'jml_virtuemart_products' I have (among others) the columns 'product_sku' and 'virtuemart_product_id'.
Now I want to add values from jml_virtuemart_products.virtuemart_product_id column into the the same column in 'blanko' from rows with where product_sku is the same.
I am trying with this query and it works partialy.
UPDATE blanko b1 SET virtuemart_product_id = (SELECT virtuemart_product_id FROM jml_virtuemart_products v1 WHEREe v1.product_sku = b1.product_sku);
The problem is that it add endless amount of rows with NULL values.
Can someone explain what I am doing wrong? I am running in circles...
Better way is to use join to update the record
update blanko b1
join jml_virtuemart_products v1 on v1.product_sku = b1.product_sku
set b1.virtuemart_product_id = v1.virtuemart_product_id

MySQL INSERT INTO single column from a SELECT query pulling data from two other tables - throwing #1062 - Duplicate entry '' for key 2 error

I've got one col (state_not_allowed) in TABLE vendor_product where I'm trying to insert values from product_catalog_varchar.value - but only if there's a sku in vendor_product that matches a sku in product_catalog where product_catalog's id equals product_catalog_varchar's id and product_catalog_varchar's attribute id = 523.
I'm basically trying to do the MySQL equivalent of an Excel VLOOPUP. I need the result of the following query:
SELECT product_catalog_varchar.value
FROM product_catalog_varchar
JOIN product_catalog
ON product_catalog.id = product_catalog_varchar.id
JOIN vendor_product
ON vendor_product.sku = product_catalog.sku
AND product_catalog_varchar.attribute_id = 523
To be inserted in to column state_not_allowed, where the sku in vendor_product = the sku in product_catalog.
I've done some research on INSERT INTO, here and on Google in general. Looks like a lot of the instruction out there is on simplier queries so I haven't been able to find a decent model to figure out what to do. I can tell you that this query doesn't work:
INSERT INTO vendor_product(`state_not_allowed`)
SELECT product_catalog_varchar.value
FROM product_catalog_varchar
JOIN product_catalog
ON product_catalog.id = product_catalog_varchar.id
JOIN vendor_product
ON vendor_product.sku = product_catalog.sku
AND product_catalog_varchar.attribute_id = 523
It throws the following error: #1062 - Duplicate entry '' for key 2
And if I got to vendor_product and look, instead of simply inserting values in to state_not_allowed, it's creating a whole new row (with no data). Clearly, I'm misunderstanding in a fundamental sense here. Help me out? Thanks folks.
This query shows the general idea of what you want to do.
insert into table2
(field1)
select distinct field1
from table1
where field1 in
(select field1
from table1
except
select field1
from table2)
The details vary from RDBMS to RDBMS. For example, Oracle uses the keyword minus instead of except. Your MySql documentation will help you with the details.
Note that while it's tempting to simplify this by using "not in" that construct tends to be slow.

Inner join A on B if B not empty, else A

Two tables:
prefix ( id, value )
---------------------
1 'hello'
2 'good afternoon'
3 'good night'
suffix ( id, value )
---------------------
1 'world'
3 'world'
I'd like to get
all from table prefix which can be joined on table suffix via id
result should look like:
prefix.id prefix.value
--------------------------
1 'hello'
3 'good night'
well - quite easy so far...
but if table suffix is empty I'd like everything from table prefix
without subselects/ctes or if.... and in one query fulfilling both conditions!
Is there any trick to get this done by some magic having-clause or tricky something else?
Just for testcases: SQL-fiddle
Well, there is a way, but I agree with others that your requirements make no (practical) sense.
Anyway, here you go:
Join the suffix table twice (each time with a left join). One join is on the id column, the other on an always true condition.
Group the results on the prefix columns you want in the output and at least one non-nullable column of the first instance of suffix.
In the HAVING clause, put a condition that the first suffix column is not null or the number of values of a non-nullable column in the second suffix instance is 0. (Obviously, every group will have the same number of rows, i.e. the count will be the same for every prefix row.)
This is the query:
SELECT prefix.id, prefix.value
FROM prefix
LEFT JOIN suffix ON prefix.id = suffix.id
LEFT JOIN suffix AS test ON 1=1
GROUP BY prefix.id, prefix.value, suffix.id
HAVING suffix.id IS NOT NULL OR COUNT(test.id) = 0;
And there's also a demo at SQL Fiddle.
You need an OR and NOT EXISTS:
SELECT
prefix.id, prefix.value
FROM
prefix
WHERE
EXISTS(SELECT 1 from suffix WHERE prefix.id=suffix.id)
OR NOT EXISTS(SELECT 1 FROM suffix)
Demo
I guess the answer is: no, you can't!
Or if you can: No, you shouldn't.

Writing a correlated sub-query in my select query

I have a funky query that works fine with static data but I need my data to be dynamic. So the static data is like this
SELECT c.my_name, c.my_id, (SELECT count(d.friendship_id) FROM another_table d WHERE d.my_id = 1 AND d.my_friends_id = 2) as count FROM myprofile c WHERE c.my_id = 1;
This returns the data I want like this:
my_name my_id count
parijat 123 1 (OR 0 if the row doesn't exist)
For reference, both another_table.my_id (foreign key), another_table.my_friends_id references myprofile.my_id (primary key). another_table.friendship_id is the primary key here and is auto incremented.
Now the actual question:
I want my subquery to be something like this:
(SELECT count(d.friendship_id) FROM another_table d WHERE d.my_id = 1 AND d.my_friends_id = CURRENT_ROW_ID)
where CURRENT_ROW_MY_ID is the c.my_id that is being selected upon in the main query.
Is this possible and if not, what should my approach be to get the results I need ?
You can do a subquery to get the current auto_increment value for that table:
select auto_increment from information_schema.tables where table_schema = 'you_db_name' and table_name = 'your_table_name'
HTH
Francisco
Sometimes I ask before I have completely explored the option. Just found out that a correlated subquery works fine even in select statements. Here is what I did to get it working:
SELECT c.my_name, c.my_id, (SELECT count(d.friendship_id) FROM another_table d WHERE d.my_id = 1 AND d.my_friends_id = c.my_id) as count FROM myprofile c WHERE c.my_id = 1;
my_id is slightly ambiguous. A better word for it would be profile_id, however dealing with a legacy database ain't fun for sure.

MySQL query - how to return placeholder value instead of NULL if no entry found

Let's say I have a table called references, which has two fields: an id and a reference field.
I want to create a query that will provide me with a reference number based upon an id. Like this:
SELECT reference
FROM references
WHERE id = x
(where x is some integer)
However if the id is not found in the table I would like the query to show -1 instead of NULL.
How can I do this?
SELECT COALESCE(reference, -1) FROM references WHERE id = x
doesn't work
Here are a few approaches:
SELECT COALESCE(MAX(reference), -1)
FROM references
WHERE id = ...
;
SELECT COALESCE(reference, -1)
FROM references
RIGHT
OUTER
JOIN (SELECT 1 c) t
ON id = ...
;
SELECT COALESCE
( ( SELECT reference
FROM references
WHERE id = ...
),
-1
)
;
(I'd go with the first one, personally, but all three work.)
if subset has cardinality 0 (elements with id = 2), there is nothing to compare, there's certainity that such (id = 2) element doesn't exist. in the other hand, if you want to find, let's say, maximum element in that empty subset, you will get unknown value (every member of superset will be a supremum and infimum of the empty set)
i'm not sure if it's correct, but imho, quite logical