How to determine if a MySQL query is valid? - mysql

Look here and here.
With the answers above, I have made this query, is it valid? If not, how can I correct it?
SELECT *,
FROM TABLE_2 t
WHERE EXISTS(SELECT IF(column1 = 'smith', column2, column1)
FROM TABLE_1 a
WHERE 'smith' IN (a.column1, a.column2)
AND a.status = 1
AND ( 'smith' IN (t.column1, t.column2)
)

To start with, the comma after select * does not belong.
Second, you alias your tables (table_2 t and table_1 a), but then you don't consistently use the aliases, so you might have issues at run time. Also from a maintenance perspective, I think most folks prefer to use aliases when declared, and no aliases otherwise.
Third, you do a comparison against cols from the t table in the outer select ('smith' in (t.column1, t.column2) ), when that appears unnecessary. You can just do it in the outer select. In other words, you can move that terminal paren to before the AND ('smith'...
As for whether it works -- I have no idea, since I don't know what you are trying to accomplish.
Combined, that would leave you with :
SELECT t.*
FROM TABLE_2 t
WHERE EXISTS (SELECT IF(a.column1 = 'smith', a.column2, a.column1)
FROM TABLE_1 a
WHERE 'smith' IN (a.column1, a.column2)
AND a.status = 1)
AND ( 'smith' IN (t.column1, t.column2)

Related

MySql - Multitable - AND is not the correct choice, but what is?

I have two tables:
mytable1
UserId (int) (primary_key)
Save (blob)
mytable2
UserId (int) (primary_key)
Save (blob)
I make the following mysql command:
UPDATE mytable1 tb1, mytable2 tb2 SET tb1.Save='', tb2 .Save='' WHERE tb1.UserId=25 AND dbSv1.UserId=25
When both tables have a user with UserId = 25, then this works and Save is set to ''. However, if one table does not have a user with UserId = 25, but the other one does, then Save is not set to '' in the one that does. This is not the behaviour I want.
OR is not the thing to use, as other Saves will be set to '' which do not have an UserId of 25. So what do I need?
Your query is using the old-school comma syntax for a join operation. (There's some problems in the SQL... dbSv1 is used as a qualifier, but it doesn't appear as a table name or table alias. We're going to assume that was supposed to be tb2.
Your query is equivalent to:
UPDATE mytable1 tb1
JOIN mytable2 tb2
SET tb1.save=''
, tb2.save=''
WHERE tb1.userid=25
AND tb2.userid=25
If a matching row is not found in either tb1 or tb2, the the JOIN operation will produce an empty set. This is expected behavior.
Consider the result set returned from this query:
SELECT tb1.userid
, tb2.userid
FROM mytable1 tb1
JOIN mytable2 tb2
WHERE tb1.userid=25
AND tb2.userid=25
when there are no rows in tb2 that satisfy the predicates, the query won't return any rows.
You could use an "outer" join to make returning rows from one of the tables optional. For example, to update mytable1 even when no matching rows exist in mytable2...
UPDATE mytable1 tb1
LEFT
JOIN mytable2 tb2
ON tb2.userid=25
SET tb1.save=''
, tb2.save=''
WHERE tb1.userid=25
If there are no rows in mytable1 that have userid=25, then this won't update any rows.
MySQL doesn't support FULL OUTER JOIN. But you try something like this, using an inline view to return a row, and then performing outer joins to both mytable1 and mytable2...
UPDATE ( SELECT 25 + 0 AS userid ) i
LEFT
JOIN mytable1 tb1
ON tb1.userid = i.userid
LEFT
JOIN mytable2 tb2
ON tb2.userid = i.userid
SET tb1.save = ''
, tb2.save = ''
SQLFiddle demonstration: http://sqlfiddle.com/#!9/6f8598/1
FOLLOWUP
A "join" is a common SQL operation. You shouldn't have any trouble finding out information about what that is what it does.
The "+ 0" isn't strictly necessary. It's a convenient shorthand in MySQL to CAST to numeric. As a test, see what MySQL returns for this:
SELECT '25' + 0
, '25xyz' + 0
, 'abc' + 0
The purpose of the inline view was to return a single row. We could have written the query to hardcode the user_id two times, and ignore what's returned from the line view ....
SELECT t1.user_id AS t1_user_id
, t2.user_id AS t2_user_id
FROM ( SELECT 'foo' AS dontcare ) i
LEFT
JOIN mytable1 t1
ON t1.user_id = 25
LEFT
JOIN mytable t2
ON t2.user_id = 25
My preference is to make it more clear that our intent is for both of the values to be the same. We could code where one of them is 23 and the other is 27. That's syntactically valid to do that. When we convert this to a prepared statement with bind placeholders...
SELECT t1.user_id AS t1_user_id
, t2.user_id AS t2_user_id
FROM ( SELECT 'foo' AS dontcare ) i
LEFT
JOIN mytable1 t1
ON t1.user_id = ?
LEFT
JOIN mytable t2
ON t2.user_id = ?
We kind of "lose" the idea that those two values are the same. To get that hardcoded value specified only one time, I have the inline view return the value we want to "match" in the ON clause of the outer joins.
SELECT t1.user_id AS t1_user_id
, t2.user_id AS t2_user_id
FROM ( SELECT ? AS user_id ) i
LEFT
JOIN mytable1 t1
ON t1.user_id = i.user_id
LEFT
JOIN mytable t2
ON t2.user_id = i.user_id
Now my intent is more clear... I'm looking for "one" user_id value. Adding the "+ 0" indicates that whatever value gets passed in (e.g. '25', 'foo', or whatever), my statement is going to interpret that as a numeric value.
inline view
I used the term "inline view". That's just a SELECT query used in a context where we usually have a table.
e.g. if i have a table named mine, i can write a query...
SELECT m.id, m.name FROM mine m
test it and see that it returns rows, yada, yada.
I can also do this: wrap that query in parens and reference it in place of a table in another statement, like this...
SELECT t.*
FROM ( SELECT m.id, m.name FROM mine m ) t
MySQL requires that we assign an alias to that, like we can do if it were a table. We call that an inline view because it's similar to the pattern we use for a stored view. Let's look at a demonstration of doing that.
(This is just a demonstration of the pattern; there's some reasons we wouldn't want to do this.)
CREATE VIEW myview
AS
SELECT m.id, m.name FROM mine m
;
Then we can do this:
SELECT t.* FROM myview t
With the inline view we're following the same pattern, but we're bypassing a separate create view statement. (That's a DDL statement that causes an implicit commit, and creating a database object.) Bypassing that, we're effectively creating a view that exists only in the context of the statement, and doing that "inline", within the statement.
SELECT t.* FROM ( SELECT m.id, m.name FROM mine m ) t
The MySQL documentation refers to the inline view as a "derived table". If we (accidentally) forget the alias, the error we get back says something like "every derived table must have a alias". The more general term, used for databases other than MySQL is "inline view".

Inner Join SQL Syntax

I've never done an inner join SQL statement before, so I don't even know if this is the right thing to use, but here's my situation.
Table 1 Columns: id, course_id, unit, lesson
Table 2 Columns: id, course_id
Ultimately, I want to count the number of id's in each unit in Table 1 that are also in Table 2.
So, even though it doesn't work, maybe something like....
$sql = "SELECT table1.unit, COUNT( id ) as count, table2.id, FROM table1, table2, WHERE course_id=$im_course_id GROUP BY unit";
I'm sure the syntax of what I'm wanting to do is a complete fail. Any ideas on fixing it?
SELECT unit, COUNT( t1.id ) as count
FROM table1 as t1 inner JOIN table2 as t2
ON t1.id = t2.id
GROUP BY unit
hope this helps.
If I understand what you want (maybe you could post an example input and output?):
SELECT unit, COUNT( id ) as count
FROM table1 as t1 JOIN table2 as t2
ON t1.id = t2.id
GROUP BY unit
Okay, so there are a few things going on here. First off, commas as joins are deprecated so they may not even be supported (depending on what you are using). You should probably switch to explicitly writing inner join
Now, whenever you have any sort of join, you also need on. You need to tell sql how it should match these two tables up. The on should come right after the join, like this:
Select *
From table1 inner join table2
on table1.id = table2.id
and table1.name = table2.name
You can join on as many things as you need by using and. This means that if the primary key of one table is several columns, you can easily create a one-to-one match between tables.
Lastly, you may be having issues because of other general syntax errors in your query. A comma is used to separate different pieces of information. So in your query,
SELECT table1.unit, COUNT( id ) as count, table2.id, FROM ...
The comma at the end of the select shouldn't be there. Instead this should read
SELECT table1.unit, COUNT( id ) as count, table2.id FROM ...
This is subtle, but the sql query cannot run with the extra comma.
Another issue is with the COUNT( id ) that you have. Sql doesn't know which id to count since table1 and table2 both have ids. So, you should use either count(table1.id) or count(table2.id)

Optimizing NOT IN and a subquery?

I have this query:
select distinct somecolumn from sometable
where something = somethingelse and
someid not in (select anotherid from another tabele where ...);
I think the query runs really slow because of the subselect. Is there a way to rewrite that so the not in and the subselect can be removed?
use LEFT JOIN
SELECT someClumn
FROM sometable a
LEFT JOIN anotherTable b
ON a.someID = b.anotherID
-- AND condition for anotherTable
WHERE a.something = somethingelse AND
b.anotherID IS NULL
for better performance, define an index on columns: someID and anotherID.
Try
select
distinct somecolumn
from
sometable t1
left join anothertable t2 on (t1.someid=t2.otherid and t2.othercondition='else')
where
t1.something='something else'
and t2.pk is null
;
And t2.otherid should be indexed.
NOTE: The WHERE clause from the subquery is in the JOIN condition.
In theory, NOT EXISTS should optimize slightly better than NOT IN, and should also be more reliable if anotherid is NULLable (details on this from a SQL Server standpoint). Though I will confess I don't know enough about MySQL to know if this query will be better:
SELECT somecolumn
FROM dbo.sometable AS s
WHERE something = somethingelse
AND NOT EXISTS
(
SELECT 1 FROM dbo.[another table]
WHERE anotherid = s.someid
);
But I suspect the real performance problem here is lack of indexes, not the presence of a subquery. Is sometable.something indexed? How about sometable.somied? And [another table].anotherid? Also the DISTINCT may require an additional sort, but is it truly necessary? If you have duplicates, that might suggest a design problem...
select somecolumn
from sometable
LEFT JOIN another tabele
ON someid = anotherid
AND (where clause from subquery)
WHERE anotherid IS NULL
AND something = somethingelse
I assume DISTINCT redundant if you have One-to-many relationship.
try this
select somecolumn from sometable
where something = somethingelse and
someid not in (select anotherid from another tabele where ...)
GROUP BY somecolumn;

Check membership of elements of one column in another, mySQL

How would I go about counting values that appear in column 1, but not column 2. They are from the same table, without using subqueries or anything fancy. They may or may not share other common column values (like col 3 = col 4) but this doesnt matter.
I have it almost working with subqueries, but cannot figure how to do it without. The only problem (I think) is it will count something twice if the primary key (composed of col1,col3,col4) are different but col1 is the same.
SELECT DISTINCT COUNT(*)
FROM mytable t1
WHERE NOT EXISTS (
SELECT DISTINCT *
FROM mytable
WHERE t1.column1 = mytable.column2
);
But like I said, I'm trying to figure this without subqueries anyways
How about:
SELECT COUNT(*)
FROM mytable mt1
LEFT JOIN mytable mt2 ON mt1.column1 = mt2.column2
WHERE mt2.column IS NULL
Please see this:
SELECT
SUM(IF(column1 = column2, 0, 1)) as c
FROM
mytable

mysql SELECT NOT IN () -- disjoint set?

I'm having a problem getting a query to work, which I think should work. It's in the form
SELECT DISTINCT a, b, c FROM t1 WHERE NOT IN ( SELECT DISTINCT a,b,c FROM t2 ) AS alias
But mysql chokes where "IN (" starts. Does mysql support this syntax? If not, how can I go about getting these results? I want to find distinct tuples of (a,b,c) in table 1 that don't exist in table 2.
You should use not exists:
SELECT DISTINCT a, b, c FROM t1 WHERE NOT EXISTS (SELECT NULL FROM t2 WHERE t1.a = t2.a AND t1.b = t2.b AND t1.c = t2.c)
Using NOT IN is not the best method to do this, even if you check only one key. The reason is that if you use NOT EXISTS the DBMS will only have to check indices if indices exist for the needed columns, where as for NOT IN it will have to read the actual data and create a full result set that subsequently needs to be checked.
Using a LEFT JOIN and then checking for NULL is also a bad idea, it will be painfully slow when the tables are big since the query needs to make the whole join, reading both tables fully and subsequently throw away a lot of it. Also, if the columns allow for NULL values checking for NULL will report false positives.
I had trouble figuring out the right way to execute this query, even with the answers provided; then I found the MySQL documentation reference I needed:
SELECT DISTINCT store_type
FROM stores
WHERE NOT EXISTS (SELECT * FROM cities_stores WHERE cities_stores.store_type = stores.store_type);
The trick I had to wrap my brain around was using the reference to the 'stores' table from the first query inside the subquery. Hope this helps (or helps others, since this is an old thread.)
From http://dev.mysql.com/doc/refman/5.0/en/exists-and-not-exists-subqueries.html
SELECT DISTINCT t1.* FROM t1 LEFT JOIN t2 ON (t1.a = t2.a AND t1.b = t2.b AND t1.c = t2.c) WHERE t2.a IS NULL
As far as I know, NOT IN can only be used for 1 field at a time. And the field has to be specified in between "WHERE" and "NOT IN".
(Edit:)
Try using a NOT EXISTS:
SELECT a, b, c
FROM t1
WHERE NOT EXISTS
(SELECT *
FROM t2
WHERE t1.a = t2.a AND t1.b = t2.b AND t1.c = t2.c)
In addition, an inner join on a, b, and c being equal should give you all non-DISTINCT tuples, while a LEFT JOIN with a WHERE IS NULL clause should give you the DISTINCT ones, as Charles mentioned below.
Well, I'm going to answer my own question, in spite of all the great advice others gave.
Here's the proper syntax for what I was trying to do.
SELECT DISTINCT a, b, c FROM t1 WHERE (a,b,c) NOT IN ( SELECT DISTINCT a,b,c FROM t2 )
Can't vouch for the efficiency of it, but the broader questions I was implicitly putting was "How do I express this thought in SQL", not "How do I get a particular result set". I know that's unfair to everyone who took a stab, sorry!
Need to add a column list after the WHERE clause and REMOVE the alias.
I tested this with a similar table and it is working.
SELECT DISTINCT a, b, c
FROM t1 WHERE (a,b,c)
NOT IN (SELECT DISTINCT a,b,c FROM t2)
Using the mysql world db:
-- dont include city 1, 2
SELECT DISTINCT id, name FROM city
WHERE (id, name)
NOT IN (SELECT id, name FROM city WHERE ID IN (1,2))