Conditional Queries Among Multiple Tables - mysql

Greetings and thank you for looking at my question, I hope you can provide some insight or direction.
I have three tables (fundamentally): 'value_meta', 'value', and 'values_visibility'. The schema follows:
TABLE 'value_meta'
COMMENT: contains a list of different values, each referencing a different 'value' table
int id PK
tinyint value1 FK to value1.value
tinyint value2 FK to value2.value
tinyint value3 FK to value3.value
...
TABLE 'value'
COMMENT: there are different value tables (for example, if it were for user profile data, there would be a value table for "occupation", "body type", and/or "education level"
tinyint id PK
varchar(255) value
TABLE 'value_visibility'
COMMENT: one value visibility entry per value[n] in the 'value_meta' table, each a boolean value. If 1, the coding query will return the value as rerefenced in 'value[n]' table. if 0, return null
int id PK
BOOLEAN 'value1_visibility'
BOOLEAN 'value2_visibility'
BOOLEAN 'value3_visibility'
....
What I want to do is create a proper MySQL query to check "for each 'value' in 'value_meta', if corresponding value entry in 'value_visibility' is 1, display value varchar. else return null". By proper I want to make it most efficient (dereived tables vs. correlated subqueries, proper conditionals and function uses... I hear ISNULL is bad).
I used to be good at query building straight out of my mind back in college but after years of not using it, I've become three straws short of a full broom. Can anyone help me? Thanks!

SELECT vm.id,
IF(vv.id IS NULL, NULL, vm1v.value) value1,
IF(vv.id IS NULL, NULL, vm2v.value) value2,
IF(vv.id IS NULL, NULL, vm3v.value) value3
FROM value_meta vm
LEFT JOIN value vmv1 ON vm.value1 = vmv1.id
LEFT JOIN value vmv2 ON vm.value1 = vmv2.id
LEFT JOIN value vmv3 ON vm.value1 = vmv3.id
LEFT JOIN value_visibility vv ON vm.id = vv.id AND vv.value1_visibility = 1
You should think about restructuring your value_meta table, is there a reason why you are storing value1 2 and 3 in the same row?

Related

MYSQL ERROR CODE: 1288 - can't update with join statement

Thanks for past help.
While doing an update using a join, I am getting the 'Error Code: 1288. The target table _____ of the UPDATE is not updatable' and figure out why. I can update the table with a simple update statement (UPDATE sales.customerABC Set contractID = 'x';) but can't using a join like this:
UPDATE (
SELECT * #where '*' contains columns a.uniqueID and a.contractID
FROM sales.customerABC
WHERE contractID IS NULL
) as a
LEFT JOIN (
SELECT uniqueID, contractID
FROM sales.tblCustomers
WHERE contractID IS NOT NULL
) as b
ON a.uniqueID = b.uniqueID
SET a.contractID = b.contractID;
If changing that update statement a SELECT such as:
SELECT * FROM (
SELECT *
FROM opwSales.dealerFilesCTS
WHERE pcrsContractID IS NULL
) as a
LEFT JOIN (
SELECT uniqueID, pcrsContractID
FROM opwSales.dealerFileLoad
WHERE pcrsContractID IS NOT NULL
) as b
ON a."Unique ID" = b.uniqueID;
the result table would contain these columns:
a.uniqueID, a.contractID, b.uniqueID, b.contractID
59682204, NULL, NULL, NULL
a3e8e81d, NULL, NULL, NULL
cfd1dbf9, NULL, NULL, NULL
5ece009c, , 5ece009c, B123
5ece0d04, , 5ece0d04, B456
5ece7ab0, , 5ece7ab0, B789
cfd21d2a, NULL, NULL, NULL
cfd22701, NULL, NULL, NULL
cfd23032, NULL, NULL, NULL
I pretty much have all database privileges and can't find restrictions with the table reference data. Can't find much information online concerning the error code, either.
Thanks in advance guys.
You cannot update a sub-select because it's not a "real" table - MySQL cannot easily determine how the sub-select assignment maps back to the originating table.
Try:
UPDATE customerABC
JOIN tblCustomers USING (uniqueID)
SET customerABC.contractID = tblCustomers.contractID
WHERE customerABC.contractID IS NULL AND tblCustomers.contractID IS NOT NULL
Notes:
you can use a full JOIN instead of a LEFT JOIN, since you want uniqueID to exist and not be null in both tables. A LEFT JOIN would generate extra NULL rows from tblCustomers, only to have them shot down by the clause requirement that tblCustomers.contractID be not NULL. Since they allow more stringent restrictions on indexes, JOINs tend to be more efficient than LEFT JOINs.
since the field has the same name in both tables you can replace ON (a.field1 = b.field1) with the USING (field1) shortcut.
you obviously strongly want a covering index with (uniqueID, customerID) on both tables to maximize efficiency
this is so not going to work unless you have "real" tables for the update. The "tblCustomers" may be a view or a subselect, but customerABC may not. You might need a more complicated JOIN to pull out a complex WHERE which might be otherwise hidden inside a subselect, if the original 'SELECT * FROM customerABC' was indeed a more complex query than a straight SELECT. What this boils down to is, MySQL needs a strong unique key to know what it needs to update, and it must be in a single table. To reliably update more than one table I think you need two UPDATEs inside a properly write-locked transaction.

SQL to update selected fields in table from view

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!)

In Oracle SQL, does null = null?

I can't test if null = null, since I don't have the permissions to create data on the database i'm using.
My question is:
If I have a table with two rows:
Row 1: ItemID = 1, CollectionID = 1, Price = null
Row 2: ItemID = 2, CollectionID = 1, Price = null
And my query is as follows:
SELECT CollectionID
FROM my_table TB
WHERE Price >= (SELECT avg(Price)
FROM my_table
WHERE TB.CollectionID = CollectionID);
Will collectionID 1 be displayed in the results?
In other words, we have null values for TB.CollectionID and for CollectionID.
Does TB.CollectionID = CollectionID ?
No, NULL is equal to no other value, including another instance of NULL (this is true for SQL in general, not just Oracle (a)).
That's because NULL is not a value, rather it's an entity meaning "unknown", "not available" or "not applicable".
That's why, if you want to check if something is NULL, you use:
where something is null
rather than:
where something = null
If you do want to treat NULL values as equal, you can do something like:
where TB.CollectionID = CollectionID
or (TB.CollectionID is null and CollectionID is null)
but that sort of goes against the whole concept of SQL tri-value logic.
And, as an aside, don't let your lack of power on your own systems limit what you can do. See SqlFiddle, for example, which can let you create and manipulate databases at will.
(a) Though, of course, some DBMS don't always follow the standard in ways that make sense, such as a certain large vendor (no names, but it starts with o and ends with racle (b)) not being able to store an zero-length string into a varchar-type field, instead coercing it to be NULL :-)
(b) Or perhaps in this case, it makes much more sense to say it starts with NULL and ends with oracle :-)

Why Mysql Query Count is different?

I have a Customer table, my client want to not physically delete any record from this table so I use a TINYINT field "IsDeleted" to keep track of deleted customers.
Now i m in a situation where i need to exclude Deleted Customers but when i tired following Query it gives me less number of records
select count(*) from customer where IsDeleted <> 1; (Count = 1477)
then the following
select count(*) from customer where (IsDeleted = 0 or IsDeleted is null); (Count = 1552)
why the above query counts are different?
why "NULL" value is not counted in " IsDeleted <> 1" check?
Please suggest.
Like Duniyadnd and triclosan point out this is caused by the column type for IsDeleted.
Change the query in the right panel so you can see the difference between using int and varchar column types. sqlfiddle.com/#!2/7bf0a/5
You cannot correct use comparing operations for NULL-values. Consider to change type of IsDeleted to enum('N','Y') with Not NULL option.
Null is not an int. As soon as you did <> 1, that means it would only look at ints. Null ideally means something that does not exist (that's why a lot of people use it instead of 0 in case you do store 0s in the table).
If you only have null, 0 and 1 values in the isDeleted column, you would probably find a difference between the two queries (1522-1477) to be the total number of nulls in your table (75).

Need Help Speeding up an Aggregate SQLite Query

I have a table defined like the following...
CREATE table actions (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
end BOOLEAN,
type VARCHAR(15) NOT NULL,
subtype_a VARCHAR(15),
subtype_b VARCHAR(15),
);
I'm trying to query for the last end action of some type to happen on each unique (subtype_a, subtype_b) pair, similar to a group by (except SQLite doesn't say what row is guaranteed to be returned by a group by).
On an SQLite database of about 1MB, the query I have now can take upwards of two seconds, but I need to speed it up to take under a second (since this will be called frequently).
example query:
SELECT * FROM actions a_out
WHERE id =
(SELECT MAX(a_in.id) FROM actions a_in
WHERE a_out.subtype_a = a_in.subtype_a
AND a_out.subtype_b = a_in.subtype_b
AND a_in.status IS NOT NULL
AND a_in.type = "some_type");
If it helps, I know all the unique possibilities for a (subtype_a,subtype_b)
eg:
(a,1)
(a,2)
(b,3)
(b,4)
(b,5)
(b,6)
Beginning with version 3.7.11, SQLite guarantees which record is returned in a group:
Queries of the form: "SELECT max(x), y FROM table" returns the value of y on the same row that contains the maximum x value.
So greatest-n-per-group can be implemented in a much simpler way:
SELECT *, max(id)
FROM actions
WHERE type = 'some_type'
GROUP BY subtype_a, subtype_b
Is this any faster?
select * from actions where id in (select max(id) from actions where type="some_type" group by subtype_a, subtype_b);
This is the greatest-in-per-group problem that comes up frequently on StackOverflow.
Here's how I solve it:
SELECT a_out.* FROM actions a_out
LEFT OUTER JOIN actions a_in ON a_out.subtype_a = a_in.subtype_a
AND a_out.subtype_b = a_in.subtype_b
AND a_out.id < a_in.id
WHERE a_out.type = "some type" AND a_in.id IS NULL
If you have an index on (type, subtype_a, subtype_b, id) this should run very fast.
See also my answers to similar SQL questions:
Fetch the row which has the Max value for a column
Retrieving the last record in each group
SQL join: selecting the last records in a one-to-many relationship
Or this brilliant article by Jan Kneschke: Groupwise Max.