SQL Searching - Two Columns Varying Values - sql

Based on a poor database design beyond my control, I am now having to deal with something new (for me). I am hoping someone can assist me.
We have two columns in the database: "Column1" and "Column2"
Column1 can have a value of Breakfast/Lunch/Dinner. Column2 is free text and can contain many things.
How can I write a query where I can look for more than one specific value in Column1 and Column2 where both conditions must be true?
Example Code:
Select *
from TestDb
where (Column1 = 'Breakfast' and Column2 like '%banana%')
and (Column1 = 'Lunch' and Column2 like '%pizza%')
The expected result is that we find all subjects who had a banana for breakfast and pizza for lunch (which is why both conditions must be true). We don't want to find subject who just had a banana for breakfast or just pizza for lunch.

If you want to find out a single answer based on two different rows, then one approach is to check for one and then see if the other also exists. You haven't given a lot of info on your schema, so a simple example might be:
SELECT 'FOUND'
FROM TestDB a
WHERE a.Column1 = '100' and a.Column2 like '%hello%'
AND EXISTS (SELECT 1
FROM TestDB b
WHERE b.Column1 = '150' and b.Column2 like '%goodbye%')
Obviously you can change 'FOUND' for whatever relevant data you'd like to return. If there's some kind of identifier you need to match on then you'd add that into the sub select, ie.: AND b.Id = a.Id.
If this doesn't give you what you want, I think you really need to give some examples in your question to clarify.

Where column1 in (100,150) and (column2 like ‘%hello%’ or column2 like ‘%goodbye%’)

You would seem to want something like this:
Select master_id
from TestDb
where (Column1 = '100' and Column2 like '%hello%') or
(Column1 = '150' and Column2 like '%goodbye%')
group by master_id
having count(*) = 2;
This assumes that the rows are unique, so two rows do not match the same condition.

may be we can use union ? I ran into similar thing way back, Union helped me. Please have a look, if it helps.
select * from table1 where column1 ='val1' and column2 ='val2'
union
select * from table1 where column1='val3' and column2 ='val4'
Joins are interesting, self joins are interesting too.

Related

SQL select not viewing correct data

I have earlier been using MySQL and there I could get the correct response with this select query.
SELECT *
FROM TABLE
WHERE COLUMN1 NOT IN ('Done', 'Closed')
AND COLUMN2 IN ('Dude1', 'Dude2', 'Dude3')
Now we have changed db to oracle and there it leaves out if COLUMN1 have no value in it. In another words, it's null/empty.
I have tried what I believe would be the correct way to make the query but it behaves not as I was hoping. Maybe someone have a solution that I could retrieve same information as in MySQL.
Last attempt I ended up with this query
SELECT *
FROM TABLE
WHERE COLUMN1 NOT IN ('Done', 'Closed')
OR COLUMN1 IS NULL
AND COLUMN2 IN ('Dude1', 'Dude2', 'Dude3')
When I run this query I get all that have null/empty as value in COLUMN1 but I also get Dude6 in COLUMN2 from the reply... and I can't figure out how I could do it any other way..
If I remove
OR Column1 IS NULL
I won't get the mysterious Dude6 as responses but I also won't get the rows where column1 is empty/null and with Dude1 in it for example..
You just need parentheses around your OR expression:
SELECT *
FROM TABLE
WHERE
(COLUMN1 NOT IN ('Done','Closed') OR COLUMN1 IS NULL)
AND COLUMN2 IN ('Dude1','Dude2','Dude3')

I have million rows in one table. I am tolde to use union instead on in clause for performance reason. is it true?

We have million rows in one table.
our select:
select * from tableA where column1 in ("a", "b", "c", "d") and where column2 = "abc";
and we have unique index on column1 and column2 combined.
I was told to swtich to :
select * from tableA where column1 = "a" and column2 = "abc"
union
select * from tableA where column1 = "b" and column2 = "abc"
union
select * from tableA where column1 = "c" and column2 = "abc"
union
select * from tableA where column1 = "d" and column2 = "abc";
We could have from 1 to 100 different values in IN clause. So is it better to run one statement with IN clause or run 100 statement and perform union.
Who told you to switch? Did they provide some evidence that the second approach is actually more efficient in your environment with your data?
Unless your statistics are woefully incorrect or there is way more going on, I would find it very unlikely that the UNION approach would be more efficient than an IN. If you were going to break up the query, using UNION ALL would be more efficient than using UNION because it wouldn't force extra sorts to check for and eliminate (non-existent) duplicate rows. Assuming a relatively recent version of Oracle, I would expect the optimizer to be able to internally rewrite the UNION ALL query as an IN.
Given that you have the table in question, though, you should be able to evaluate the actual performance of the two options in your actual environment. You should be able to see whether one approach consistently outperforms the other, whether one does less logical I/O than the other, etc. You should also be able to determine whether the two queries actually generate different plans. If the UNION ALL approach is more efficient, I'd strongly consider looking at the statistics that have been gathered on your table and index(es) to determine why the optimizer isn't finding the more efficient plan with the IN statement.
If you have a unique index on column1, column2 -- in that order -- then the version with union will definitely take advantage of the index. As mentioned in a comment, you should use union all rather than union. This eliminates the step of removing duplicates (even if there are none). This would be a handful of index lookup operations and should go quite fast.
Whether Oracle uses an index as desired for the first version is somewhat open:
where column1 in ('a', 'b', 'c', 'd') and column2 = 'abc'
Most database would not use an index optimally in this case. If a database used the index, it would use the index for column1 lookups and then scan the index comparing values to column2. Oracle might have some additional smarts that will use an index effectively here.
However, it is easy to fix things. If you have an index on column2, column1, then that index would be used for the where clause.
Just to provide another alternative, the statement could be written as:
select * from tableA where (column1 = "a" and column2 = "abc")
or (column1 = "b" and column2 = "abc")
or (column1 = "c" and column2 = "abc")
or (column1 = "d" and column2 = "abc") ;

How can I get the name of a column that contains XYZ in Oracle?

Problem
I have searched online for this but can't seem to find a way to do it. I'm by no means an Oracle expert as I've only been working with it for around a month. Is it possible, and if so, how can I get the name of a column that contains XYZ in Oracle?
Please see the below example to better explain what I mean.
Example
I run a query such as:
SELECT * FROM Customers WHERE CustomerNo = 1;
I see results such as:
COLUMN1 COLUMN2 COLUMN3 COLUMN4 COLUMN5
A001 Y N 10.10 Y
I'm having trouble explain this (I've re-written this 2 or 3 times already) so apologies in advance if it makes no sense to you, but what I am looking for is for example:
SELECT column_name FROM Customers WHERE CustomerNo = 1 AND any_column = Y;
Resulting in:
COLUMN2
COLUMN5
There is no way to do a * with a comparison in Oracle (or really in any other database). You can, however, use in to facilitate the comparisons:
SELECT column_name
FROM Customers
WHERE CustomerNo = 1 AND 'Y' in (column2, column3, column5);
You can get the list of columns in a table from a system view such as all_tab_columns, if you don't want to type them out individually.

Is there a single SQL (or its variations) function to check not equals for multiple columns at once?

Just as I can check if a column does not equal one of the strings given in a set.
SELECT * FROM table1 WHERE column1 NOT IN ('string1','string2','string3');
Is there a single function that I can make sure that multiple columns does not equal a single string? Maybe like this.
SELECT * FROM table1 WHERE EACH(column1,column2,column3) <> 'string1';
Such that it gives the same effect as:
SELECT * FROM table1 WHERE column1 <> 'string1'
AND column2 <> 'string1'
AND column3 <> 'string1';
If not, what's the most concise way to do so?
I believe you can just reverse the columns and constants in your first example:
SELECT * FROM table1 WHERE 'string1' NOT IN (column1, column2, column3);
This assumes you are using SQL Server.
UPDATE:
A few people have pointed out potential null comparison problems (even though your desired query would have the same potential problem). This could be worked around by using COALESCE in the following way:
SELECT * FROM table1 WHERE 'string1' NOT IN (
COALESCE(column1,'NA'),
COALESCE(column2,'NA'),
COALESCE(column3,'NA')
);
You should replace 'NA' with a value that will not match whatever 'string1' is. If you do not allow nulls for columns 1,2 and 3 this is not even an issue.
No, there is no standard SQL way to do this. Barring any special constraints on what the string fields contain there's no more concise way to do it than you've already hit upon (col1 <> 'String1' AND col2 <> 'String2').
Additionally, this kind of requirement is often an indication that you have a flaw in your database design and that you're storing the same information in several different columns. If that is true in your case then consider refactoring if possible into a separate table where each column becomes its own row.
The most concise way to do this is
SELECT * FROM table1 WHERE column1 <> 'string1'
AND column2 <> 'string1'
AND column3 <> 'string1';
Yes, I cut & pasted that from your original question. :-)
I'm more concerned why you're wanting to compare against all three columns. It sounds like you might have a table that needs normalization. What are the actual columns of column1, column2 and column3. Are they something like phone1, phone2, and phone3? Perhaps those three columns should actually be in a subtable.

Matching two columns in MySQL

I'm quite new to SQL and have a question about matching names from two columns located within a table:
Let's say I want to use the soundex() function to match two columsn. If I use this query:
SELECT * FROM tablename WHERE SOUNDEX(column1)=SOUNDEX(column2);
a row is returned if the two names within that row match. Now I'd also like to get those name matches between column1 and column2 that aren't in the same row. Is there a way to automate a procedure whereby every name from column1 is compared to every name from column2?
Thanks :)
p.s.: If anyone could point me in the direction of a n-gram/bi-gram matching algorithm that is easy for a noob to implement into mysql that would be good as well.
If your table has a key, say id, you can try:
select A.column1, B.column2
from tablename as A, tablename as B
where (A.id != B.id) and (SOUNDEX(A.column1) = SOUNDEX(B.column2))
You can join the table to itself on that relationship as such:
SELECT * FROM tablename t1 JOIN tablename t2
ON SOUNDEX(t1.column1) = SOUNDEX(t2.column2);