MySQL - selecting rows with some values being null - mysql

I have the values of 'foo', 'bar' and 'buz'. I'd like to select rows 1 and 3 from the following table:
+----+------+------+------+
| Id | Col1 | Col2 | Col3 |
+----+------+------+------+
| 1 | foo | | |
| 2 | foo | bar | buz |
| 3 | | bar | buz |
+----+------+------+------+
What would be the optimal MySQL query for that considering the following:
empty spaces are null
I have n columns where 4 < n < 20
there will be couple hundred rows
I will be selecting particular combinations of columns in a row where only provided values can be set and the rest needs to be null. E.g.: I need a query of this kind
SELECT * FROM my_table WHERE Col1 = foo OR (Col2 = bar AND Col3 = buz);
that'll return only rows 1 and 3.
In other words I'd like to have a query that returns rows only with certain combinations of values, not the rows where coln = 'some_val' and we dont' care about the others.
The reason for this is that I'd like to have 5 combinations in 1 query and I'd like to avoid writing (...) coln <> NULL(...) everywhere.
I'd appreciate your suggestions.

You would use where clause. To find the three values:
where 'foo' in (col1, col2, col3, . . . ) or
'bar' in (col1, col2, col3, . . . ) or
'buz' in (col1, col2, col3, . . . )
Limiting the results to those three values is more difficult. You need to do a comparison on each column:
where ( (col1 in ('foo', 'bar', 'buz') or col1 is null) or
(col2 in ('foo', 'bar', 'buz') or col2 is null) or
. . .
)
You can use a spreadsheet to generate the code, if you don't want to type it all in.
Another alternative would be the following kludge:
where replace(replace(replace(concat_ws('', col1, col2, col3, . . . ),
'foo', ''
), 'bar', ''
), 'buz', ''
) = ''
This concatenates the values together and then removes the ones you care about. If everything is removed, then the row matches.
As a note: this problem suggests that you have a poor data structure. You are storing things in columns that should be in rows.

I would like the db to assume, that for every field not provided in a query it needs to be NULL for the whole row to be returned. Even if provided values match.
In that case, the trivial solution would be:
SELECT * FROM my_table WHERE
(Col1 = 'foo' AND Col2 IS NULL AND Col3 IS NULL) OR
(Col1 IS NULL AND Col2 = 'bar' AND Col3 = 'buz') OR
/* ... */
In many cases, this can be a perfectly good solution. However, it can sometimes suffer suboptimal performance due to MySQL's poor index use for OR queries. In particular, if you have a combined index on all the columns, and want to make full use of it, it can be better to rewrite the query to use UNION (or UNION ALL) instead of OR:
SELECT * FROM my_table WHERE
(Col1 = 'foo' AND Col2 IS NULL AND Col3 IS NULL) UNION ALL
SELECT * FROM my_table WHERE
(Col1 IS NULL AND Col2 = 'bar' AND Col3 = 'buz') UNION ALL
/* ... */
See this SQLFiddle for a demonstration of the two techniques, and compare the execution plans (the execution times are rather meaningless with such a small table; you'd need many more rows to see any systematic difference). Looking at the KEY_LEN and REF columns, you can see that, for the OR query, MySQL ends up using the index only for Col1, whereas with UNION ALL, it can use the whole index.

Related

Find rows in which id can be in one of several columns

Is there a more elegant SQL query for selecting rows in which ID can be in col1 or col2 or col3, etc. without a long condition containing plenty of OR operators ?
I'd like to produce the same result as the following query:
SELECT *
FROM tab
WHERE (col1 = _x_ OR col2 = _x_ OR col3 = _x_);
You can use something like :
select * from tab where 'x' in (col1, col2, col3...)

How to get all data from Tables in SQL with similar name in R

i am new to R and i want to merge multiple Tables from my SQL DB with R.
The problem is not to merge them, but to get the query. I have a lot of tables with similar name(just numbers at the end are different) in the DB.
Now i want the data in these tables to work with them in R. I already tried to import these table names from a .txt document and this works. But i canĀ“t use the function dbGetQuery(connection, "SELECT * FROM...")
right with with method because it allows only the real table name and no variable with multiple names.
Over that i tried
dbGetQuery(connection,
"SELECT * FROM INFORMATION_SCHEMA.tables WHERE TABLE_NAME LIKE '..._%'")
but this gives just table names and not the data in there.
I hope anyone can understand my problem.
Just concatenate the SQL query using paste or sprintf function:
dbGetQuery(connection, sprintf("select * from %s", table_name))
By executing this for each table_name obtained from the INFORMATION_SCHEMA.tables query, you will have tables' data in R.
Import all tables to list and merge in R. (Included LIMIT 100 for test purposes)
tables <- dbGetQuery(con, {"
SELECT concat(TABLE_SCHEMA,'.',TABLE_NAME) tab
FROM INFORMATION_SCHEMA.tables
WHERE TABLE_NAME LIKE 'pattern%'"})$tab
data <-
lapply(tables, function(table){
dbGetQuery(con, sprintf({"
SELECT *
FROM %s
LIMIT 100
"}, table)
)
})
library(magrittr);library(dplyr)
data %<>% bind_rows()
Or if one wants to keep it in MySQL, and is sure that tables have same structure, function below returns query which union all tables matching pattern:
union_all_mysql <- function(pattern){
tables <- dbGetQuery(con, sprintf({"
SELECT concat(TABLE_SCHEMA,'.',TABLE_NAME) tab
FROM INFORMATION_SCHEMA.tables
WHERE TABLE_NAME LIKE '%s'"}, pattern)
)$tab
query <-
lapply(tables, function(table)
sprintf("select * from %s", table) ) %>%
unlist
paste(query,collapse=" union ")
}
union_all_mysql("users_%")
I wouldn't do this in R, I would do it in Workbench directly. First create a new table to hold the merged results:
CREATE TABLE AllData (
id INT NOT NULL PRIMARY KEY,
col1 varchar(55), -- replace with your actual column types
col2 int,
...
)
Then just do the following INSERT INTO ... SELECT:
INSERT INTO AllData (col1, col2, col3, ..., colN)
SELECT col1, col2, col3, ..., colN
FROM table1
UNION ALL
SELECT col1, col2, col3, ..., colN
FROM table2
UNION ALL
...
SELECT col1, col2, col3, ..., colN
FROM tableM
The above assumes M tables each having the same N columns, but you will have to fill in the blanks yourself.

MySQL How to split string from column by " " and insert result separated by , into another column?

I would like to ask how to split string from column (all rows in table) by " " and insert result separated by , into another column in same table?
Many thanks for any advice.
Table struct example:
------------------------------------------
| Original string | Spliced string |
------------------------------------------
| Some string 001 | Some,String,001 |
------------------------------------------
If I needed to "split" a string on a delimiter, I'd likely make use of the nifty SUBSTRING_INDEX function. But there are a few quirks to be aware of.
The approach I would take would certainly be to write a SELECT statement first. That would include the expression(s) in the SELECT list that return the "separated" values that I wanted to assign to another column. I'd get those expressions tested using a SELECT statement, before I wrote an UPDATE statement.
SELECT t.id
, t.column_i_want_to_split
, expr1
FROM mytable t
ORDER BY t.id
To test specific cases, I'd make use of an inline view
SELECT t.id
, t.note
, t.val
, expr1
FROM ( SELECT 1 AS id, 'empty string test' AS note, '' AS val
UNION ALL SELECT 2, 'null', NULL
UNION ALL SELECT 3, 'one space', ' '
UNION ALL SELECT 4, 'four spaces', ' '
UNION ALL SELECT 5, 'test5', ' abc def '
UNION ALL SELECT 6, 'test6', 'g hi kl m'
) t
ORDER BY t.id
Once I had the expression(s) returning the values I want to assign to another column, I'd convert the SELECT into an UPDATE statement. To process all rows, omit the WHERE clause.
UPDATE mytable t
SET t.another_column = expr1
Without a more definitive specification, or at least some concrete examples of what you are attempting to achieve, we're just guessing. Given only a general description of the problem, all we can offer is some general advice.

sql query using LIKE or MATCH

I have a table of 13 columns , out of those i have 5 columns that contains let say string data or VARCHAR data.
Now i have a string let say "abc".
I want to write a sql query to get all the rows that have this "abc" string in those 5 columns. "abc" can be the data of the column of part of the data of the column.
I used LIKE function
SELECT * FROM table WHERE col1 OR col2 OR col3 OR col4 OR col5 LIKE '%abc%';
but it didnt worked n got back all the rows.
I dont know how use MATCH function either, I m nt good in sql. so can anyone help.
You can specify condition multiple times:
SELECT *
FROM table
WHERE col1 LIKE '%abc%'
OR col2 LIKE '%abc%'
OR col3 LIKE '%abc%'
OR col4 LIKE '%abc%'
OR col5 LIKE '%abc%';
This will be really slow because you have multiple OR and non-SARGable condition.
Alternatively:
SELECT *
FROM table
WHERE CONCAT_WS('^', col1,col2,col3,col4,col5) LIKE '%abc%';
Using MATCH (preferred solution that utilizes full-text index):
SELECT *
FROM table
WHERE MATCH(col1, col2,col3,col4, col5) AGAINST ('abc');
SqlFiddleDemo
Keep in mind that to use MATCH you need to create index first:
ALTER TABLE tab ADD FULLTEXT ft_index_name (col1,col2,col3,col4,col5);

MySQL regular expression to return all records where at least one word matches between 2 columns

My database has 2 columns that contain text. When it has say 3 records as follows:
rec# col1 col2
1 my name is fred is
2 john mike
3 today not sat not it sat
I would appreciate help constructing a regular expression that will return record numbers:
1 -> because "is" matches
3 -> because "'not" and "sat" match (i.e. at least one match exists)
I think you can do this as:
select t.*
from table t
where col1 rlike replace(col2, ' ', '|');
This turns col2 into a regular expression. So, note that this answer will be very sensitive to the contents of col2. If it contains regular expression special characters, then this probably will not work.
What do you mean by "matches"?
This SELECT will find the rows where col1 contains at least one of the words is or not or sat:
SELECT rec_num
FROM tbl
WHERE col1 REGEXP '[[:<:]](is|not|sat)[[:>:]]';
This says that at least one of those words exists in both col1 and col2:
SELECT rec_num
FROM tbl
WHERE col1 REGEXP '[[:<:]](is|not|sat)[[:>:]]'
AND col2 REGEXP '[[:<:]](is|not|sat)[[:>:]]';
Change AND to OR to ask if one of the words exists in either (or both) column.
If you need the same word (is/not/sat) to match both col1 and col2, that is more complex:
SELECT rec_num
FROM tbl
WHERE ( col1 REGEXP '[[:<:]]is[[:>:]]'
AND col2 REGEXP '[[:<:]]is[[:>:]]' )
OR ( col1 REGEXP '[[:<:]]not[[:>:]]'
AND col2 REGEXP '[[:<:]]not[[:>:]]' )
OR ( col1 REGEXP '[[:<:]]sat[[:>:]]'
AND col2 REGEXP '[[:<:]]sat[[:>:]]' );
If you mean something else, practice asking precise questions.
Addenda
It is not practical in SQL to discover which words (if any) are in common between two text fields. Such a task would be better done in an application programming language (PHP, Java, Perl, ...).