Let's say I have an arbitrary MySQL query called query1 that runs on a table table1 and generates a subset of the rows of that table. From a functional perspective, this query is just yielding a boolean for each row, where if the boolean is true, the row is returned in the results, and if it is false, it is not (or vice-versa, w.l.o.g.).
Let's also say that I'm taking a JSON blob, parsing it into a row, and adding the row to table1 every minute. I want to test and see if query1 returns true or false on the row immediately after I add it.
Is there a way to perform that test without actually adding the row somewhere in the database and then seeing if the row is returned by the query? My intuition is that it should be possible to 'emulate' the query on the new data without actually adding it anywhere, but I'm not familiar with how (if it's even possible in the first place).
You could use a derived table. E.g.:
select max(id) from (select 1 id, 'test' name from dual) as t;
In the above example 1 and 'test' represent the row you would need to emulate being present in the table and select max(id) from t represent query1.
You can use SQL to evaluate expressions without touching any table.
Example:
mysql> set #age = 29;
mysql> set #state = 'CA';
mysql> select #age > 30 and #state = 'CA';
+-----------------------------+
| #age > 30 and #state = 'CA' |
+-----------------------------+
| 0 |
+-----------------------------+
This example returns 0 (false) because the #age variable is not greater than 30.
You are certainly free to use values you extract from your JSON data (or any other source) as terms in an SQL expression like this.
It isn't required to use user variables as I showed in my example above. You can just use literal values too. I just showed using variables because it might be more clear to test your query conditions if you use variable names that match your column names.
Related
I have a Mysql database that contains some category ids on it which stores comma-separated values on a table.
sql table view
By using select * from style where categories like '%8,%'; it returns all the values end with 8. For example, if the table rows have two values like 8 and 148 it returns both rows. But I want to get only the rows that contain 8. How to do it
Storing multiple values in a single column is a denormalised design that will almost always cause you problems. However you need to add commas to both sides and compare:
select *
from Style
where concat(',',Categories,',') like '%,8,%';
Like everyone else: normalize your data. But if you can't mySQL supports find_in_set() for set datatypes which this appears to be.
DEMO dbfiddle.uk
DOC LINK: Find_in_set()
DOC LINK: SET data type
SQL
With CTE as (SELECT 'T-Shrits' as baseCategory, '8,21,75,87,148' categories UNION ALL
SELECT 'T-Shrits' as baseCategory, '8,21,75,87,148' categories UNION ALL
SELECT 'T-Shrits - Long Sleeve' as baseCategory, '8,21,75,87,148,92' categories UNION ALL
SELECT 'T-Shrits' as baseCategory, '21,75,87,100,148' categories)
SELECT * FROM CTE where find_in_set(8,categories) >0
OR we can use a boolean evaluation and eliminate the > 0
SELECT * FROM CTE where find_in_set(8,categories)
Giving us:
+------------------------+-------------------+
| baseCategory | categories |
+------------------------+-------------------+
| T-Shrits | 8,21,75,87,148 |
| T-Shrits | 8,21,75,87,148 |
| T-Shrits - Long Sleeve | 8,21,75,87,148,92 |
+------------------------+-------------------+
Notes
Find_in_set() returns the Returns a value in the range of 1 to N in the pseudo array of the value being searched. We need to ensure the result is greater than 0 (or treat it as a Boolean) in order for the searched value to "exist" within a record column.
The engine didn't return my 4th union value in CTE because it doesn't have an "alone" 8 value
If we searched for just 100 it would return that last record.
This function comes at a cost of performance on large datasets; which if data was normalized and indexed, you wouldn't have.
So why does this exit? For small enumerated lists or properties. It's still not ideal but if you have just a few using it "can" make sense. but in a very limited use case and often is missused.
This design violates 3rd normal form. Which is why most RDBMS designs cringe when it's brought up as it's not scalable.
as to why people are up in arms about multi value columns: Read this or This
You can also use rlike and in fact it is much better than like as it has much more options.
* = repetition of what is in front of it zero or more times
. = Equivalent to any character including none
^ = Anchor start (Forces that begins with ...)
$ = final anchor (forces it to end with ....)
[ ] = [ RST ] Contain an R or S or T but only one
[^] = DENY IT
And many more options
select * from style where concat(',',categories,',') rlike '*,8,*';
I have a bit of an odd mysql query I need to run.
I am passing two arguments to a WHERE clause, $source and $destination (via php). I want mysql to simply return nothing if $source and destination are the same number. Otherwise do the lookup in the DB and pull the record.
Query:
SELECT count(id) AS count
FROM approval
WHERE source=1 AND destination=2 AND approved!=0
Now this will return all rows where source is one and destination is two.
BUT! I want it it to return nothing if source and destination are the same number.
Some rows in certain cases may contain the same number, but that happens in a different potion of the code, in this specific select I want it only to perform the search if source and destination are different. I looked at comparison operators but those all seem to be checking the argument against a value in the column, rather than an argument against an argument.
I think this query does what you want
SELECT count(id) AS count
FROM approvals
WHERE source = $source AND destination = $destination AND approved <> 0
HAVING $source <> $destination;
That would be a simple matter of including:
and source <> destination
in the query. This will exclude rows where the columns are the same which, because of the other clauses, means the arguments you've provided are the same as well.
This will probably still go out to the database but it's guaranteed to return a count of zero (assuming no NULLs of course - if you want to handle those, you'll need extra clauses with IS [NOT] NULL). If you don't even want to go out to the database if your two arguments are the same, the time to do that is in your PHP code by comparing the arguments beforehand.
By way of example, the following DDL:
create table xyzzy (a int, b int);
insert into xyzzy (a,b) values (1,1);
creates a table with one row. When you execute the queries:
SELECT count(a) AS x FROM xyzzy WHERE a = 1 AND b = 1;
SELECT count(a) AS x FROM xyzzy WHERE a = 1 AND b = 1 and a <> b;
you'll get the rows 1 (equivalent to what you currently have) and 0 (equivalent to the change I've proposed).
If you really want to use the parameters instead of the columns, that will work as well:
SELECT count(a) AS x FROM xyzzy WHERE a = 1 AND b = 1 and 1 <> 1;
This also returns a count of zero and is equivalent to just tacking:
and $source <> $destination
onto your query. If the two numbers are identical, this will boil down to and false which will result in a count of zero.
So will either of:
and source <> $destination
and $source <> destination
for that matter.
I'm trying to run a SQL SELECT statement against a column that is of type SET. The table is called myTable and the columns in myTable are called base_props and names. The base_props column is of type SET. The values in base_prop are vb,nt, cnt,poss and loc. So I would like to SELECT entries from the column 'name' where base_props have both the values, vb and poss. The results I'm looking to get may have values other than just vb and poss. So to be clear I would like to select all entries that have the values vb and poss regardless if they have other values as well. I've tried the following SQL queries but I can't get the desired results.
SELECT name from myTable WHERE base_props = 'vb' AND base_props = 'poss'
That query returns an empty result set. I've tried using FIND_IN_SET() and IN() but I couldn't get anywhere with that. I've written SQL statements before but never had to deal with columns that are type SET. Any help is appreciated.
The only thing I can come up with is using the LIKE keyword:
SELECT name FROM myTable WHERE (base_props LIKE '%vb%' AND base_props LIKE '%poss%');
This will make sure both vb and cnt are in the base_props column. Of course you can use cnt, nt and loc in there, or any number of base_props values in the sql, just add more AND statements.
OR as a deleted answer by samitha pointed out, you can use FIND_IN_SET:
SELECT name from myTable WHERE FIND_IN_SET('vb', base_props) AND FIND_IN_SET('poss', base_props);
Comment (by spencer7593): "both of these work, but there is a slight difference. The LIKE operator will actually match any member that includes the search string anywhere in a term; the FIND_IN_SET function will only match an exact member. It's also possible to search for members in set by the order they appear in the SET definition, using the MySQL BITAND operator: for example, to match the 1st and 4th members of the set: WHERE base_props & 1 AND base_props & 8". So for example, if you have 'a' and 'aaa' in your set, then using the LIKE "%a%" method will also return rows containing 'aaa'.
Conclusion: use the FIND_IN_SET solution since it will work for all cases.
FIND_IN_SET return index, Try this
SELECT name from myTable WHERE FIND_IN_SET(base_props, 'vb') > 0 AND
FIND_IN_SET(base_props, 'poss') > 0
So my query is the following, which may return many results:
SELECT P_CODE, NAME FROM TEST.dbo.PEOPLE
WHERE NAME LIKE '%JA%'
AND P_CODE LIKE '%003%'
AND DOB LIKE '%1958%'
AND HKID = ''
AND (MOBILE LIKE '%28%' OR TEL LIKE '%28%')
I would like to integrate this into a Stored Procedure (or View?) so that it will only return a result if the query results in exactly 1 row. If there's 0 or > 1, then it should return no results.
If you just want to return an empty resultset in cases other than 1:
;WITH x AS
(
SELECT P_CODE, NAME, c = COUNT(*) OVER()
FROM TEST.dbo.PEOPLE
WHERE NAME LIKE '%JA%'
AND P_CODE LIKE '%003%'
AND DOB LIKE '%1958%'
AND HKID = ''
AND (MOBILE LIKE '%28%' OR TEL LIKE '%28%')
)
SELECT P_CODE, NAME FROM x WHERE c = 1;
Otherwise, you'll have to run the query twice (or dump the results to intermediate storage, such as a #temp table) - once to get the count, and once to decide based on the count whether to run the SELECT or not.
Effectively you want something akin to FirstOrDefault() from the Linq-to-SQL implementation but done on the server-side which means you will need to execute the query in a stored procedure, dumping the results into a temp table variable and then access ##ROWCOUNT afterwards to get the number of rows that were returned and then decide whether or not to forward the results on to the caller. If you do, be sure to use TOP 1 in the query from the temp table so that you only get a single result out as you desire.
UPDATE:
I described the alternate solution from what Aaron describes in his answer (which I like better).
Removed unnecessary TOP specifier in solution specification.
I'm trying to get column names from a table. I want to supply the row id and I want only the column names for which the value of that column for the specific row (identified by the id) is "true" (my table has a bunch of boolean fields).
I want something like:
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME.value = true
AND TABLE_THE_COLUMN_IS_FROM.id = "some_id"
Where .value would be variable, basically checking each column to see if it were true.
I know I can just get the row's values and iterate through, returning only those with value true, but I wanted to see if there is a way to do it all in one step. Thanks in advance to anyone who knows!
There is no means in one query to dynamically scan through the table's schema and inspect its values. The best way to achieve what you want is the one you suggested: query for the row client-side and then cycle through the columns searching for the values you seek. The other alternative is to query for the table schema using the INFORMATION SCHEMA views client-side, build a SQL statement with a where clause that looks for a True value in all the boolean columns, execute that and inspect the results.
This is probably going to be rather ugly no matter how you cut it, but here's one option:
select columns.column_name
from bool_table
inner join information_schema.columns
on columns.table_schema = 'your_db'
and columns.table_name = 'bool_table'
and ((columns.column_name = 'bool_1' and bool_table.bool_1 = 1)
or (columns.column_name = 'bool_2' and bool_table.bool_2 = 1))
where bool_table.id = 25
You could also potentially query information_schema.columns to dynamically generate the list of column statements so that you could dynamically generate the query, and even execute it in a stored proc using dynamic sql in mysql.