Opposite function of find_in_set in mysql - mysql

SELECT find_in_set("1","1,2,3,4,5"); //return 1
Is there any function in mysql that can return non matching value from set of value like
SELECT find_in_set("1","1,2,3,4,5");
Expected output is : 2,3,4,5
Help me If any function.

As per my knowledge, i dont think there is any function exist in mysql that gives you result except given input.
But, You can get what you want by doing this..
you can modify it as per your requirement.
SELECT * FROM table_name WHERE NOT find_in_set("1","1,2,3,4,5");

You can always use replace():
select replace(concat(',', '1,2,3,4,5', ','), concat(',', '1', ','), '')
This puts delimiters at the beginning and end of both lists so 10 won't be confused with 100. If this isn't a problem, then you don't need the delimiters.

Here's how you can do it by using SUBSTRING_INDEX.
But make sure to put "," before and after the value otherwise it will split by value which is available in different position i.e if look for 12 it will also split where value is 123.
SELECT CONCAT(SUBSTRING_INDEX("11,22,33,44,55", ',22,', 1), ",", SUBSTRING_INDEX("11,22,33,44,55", ',22,', -1));
If you are fine with this solution then you should create a function eg. FIND_NOT_IN_SET and pass two params and return final string.
I also posted another answer on How to select all the 'not' part against the 'in' set in MySQL? using prepared statement.

Using a series of UNIONed constants instead of a single value and a comma separated field:-
SELECT a.i
FROM
(
SELECT 1 i UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5
) a
LEFT OUTER JOIN
(
SELECT 1 i
)
ON a.i = b.i
WHERE b.i IS NULL
Or if only ever 1 value you are looking for:-
SELECT a.i
FROM
(
SELECT 1 i UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5
) a
WHERE a.i != '1'

Related

FULL TEXT only select if at least X words match

SELECT source, id
FROM memory_row
WHERE memory_id =10
AND MATCH(source)
AGAINST ('girl*, appears*, cool*, pragmatic*, things*, first*, glance*, actually*, warm*,
trusting*, created*, Design*, Children*, Togetsu*, existed*, solely*, activate*, Strings*,
experienced*, emotional*, damage*, young*, from*, experiments*, conducted*, her*, cruel*,
researchers*, sent*, Randall*, family*, treatment*, after*, sealing*, memories*, small*, world*,
older*, adoptive*, sister*, Naomi*')
The above query returns results in which only a few words match. I want to only return results that contain at least X of the terms that are being matched against. In the example above, that number can be 10. That means that the column must contain at least 10 fulltext matches to be returned.
How can I do this?
EDIT
One answer suggested the following. I get an error, "Incorrect arguments to AGAINST".
select m.*
from memory_row m
where
memory_id = 10
and (
select count(*)
from (
select 'girl*' word
union all select 'appears*'
union all select 'actually*'
union all select 'girl*'
union all select 'cool*'
union all select 'pragmatic*'
union all select 'things*'
union all select 'first*'
union all select 'glance*'
union all select 'actually*'
) w
where match(m.source) against(w.word)
) >= 5
I am unsure that there is an easy way to do what you want. You might need to enumerate the values as rows, and then use a correlated subquery to compute the count of matches:
select m.*
from memory_row m
where
memory_id = 10
and (
select count(*)
from (
select 'girl*' word
union all select 'appears*'
...
) w
where match(m.source) against(w.word)
) >= ?
Where the qestion mark represents the minimum number of rows that should match.
Or, in very recent versions of MySQL:
select m.*
from memory_row m
where
memory_id = 10
and (
select count(*)
from (values row('girl*'), row('appears*'), ...) w(word)
where match(m.source) against(w.word)
) >= ?
One method is to use match to get an initial set of documents. And then additional logic afterwards:
SELECT mr.*
FROM (SELECT source, id
FROM memory_row
WHERE memory_id = 10 AND
MATCH(source) AGAINST ('girl*, appears*, cool*, pragmatic*, things*, first*, glance*, actually*, warm*,
trusting*, created*, Design*, Children*, Togetsu*, existed*, solely*, activate*, Strings*,
experienced*, emotional*, damage*, young*, from*, experiments*, conducted*, her*, cruel*,
researchers*, sent*, Randall*, family*, treatment*, after*, sealing*, memories*, small*, world*,
older*, adoptive*, sister*, Naomi*')
) mr
WHERE ( (source like '%girl%') +
(source like '%actually%') +
. . .
) >= 10;
Note: This is not exactly the same logic, because it is just looking for strings. If you want more precise logic, you can use regular expressions, but that might not be necessary.

MySQL filter results from a SubSelect using AND or HAVING

I have a query that looks basically like this:
Select t.id,
(
Select GROUP_CONCAT(CONCAT_WS(' ', td.id) SEPARATOR ',') as list
From Table t2
) as id_list // this will add a comma delimited list
From Table t1
Where t1.status IS NULL
And id_list IN (3)
The query result is like this...
id|id_list
--------------------
1 |1
9 |1,3,12,10,15
This does not work as MySQL will not allow me to filter by id_list as an And conditional...
I have also tried this in place of the And conditional, but it does not work also...
Having id_list IN (3)
How can I filter these results based upon the id_list matching some parameter which I set, in this case 3.
I just want to return record id 9, not 1.
Thanks
To check for a value '3' in a comma separated list e.g. '2,3,5' we can use MySQL FIND_IN_SET function.
As a demonstration:
SELECT FIND_IN_SET('3','2,3,5') returns 2
SELECT FIND_IN_SET('3','7,11') returns 0
So for the query in the question, given id_list is a comma separated list, we could do
HAVING FIND_IN_SET('3',id_list)
or equivalently
HAVING FIND_IN_SET('3',id_list) > 0
Note that the IN comparison operator does not work like the FIND_IN_SET function.
The IN is equivalent to equality comparison of individual values. For example,
SELECT 3 IN (2,3,5)
is equivalent to
SELECT 3 = 2 OR 3 = 3 OR 3 = 5
As another example:
SELECT 3 IN ('2,3,5')
is equivalent to
SELECT 3 = '2,3,5'
Just a guess:
SELECT t.id,
(
SELECT GROUP_CONCAT(CONCAT_WS(' ', td.id) SEPARATOR ',') as list
FROM Table t2
) as id_list
FROM Table t1
WHERE t1.status IS NULL
AND FIND_IN_SET(3, id_list) > 0

Redshift nested json extraction

I have a table with two columns, one column named user, one json column named js that looks like this:
{"1":{"partner_id":54,"provider_id":13},
"2":{"partner_id":56,"provider_id":8},
"3":{"partner_id":2719,"provider_id":274}}
I want to select all 'provider_id' in one column/row.So it should look like this:
user| provider_ids
0001| 13,8,274
0002| 21,36,57,12
How can I do this? Thanks in advance!
Your provided json format is not so easy to work with.
Crated table for test purposes:
create table json_test as
select '0001' as usr, '{"1":{"partner_id":54,"provider_id":13},
"2":{"partner_id":56,"provider_id":8},
"3":{"partner_id":2719,"provider_id":274}}'
as json_text
union all
select '0002' as usr, '{"1":{"partner_id":54,"provider_id":21},
"2":{"partner_id":56,"provider_id":36},
"2":{"partner_id":56,"provider_id":57},
"3":{"partner_id":2719,"provider_id":12}}'
as json_text;
Query to return results:
with NS AS (
select 1 as n union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6 union all
select 7 union all
select 8 union all
select 9 union all
select 10
)
select usr,
listagg(trim(TRIM(split_part(SPLIT_PART(js.json_text, '},', NS.n),'"provider_id":',2)),'}'),',') within group(order by null) AS t
from NS
join json_test js ON true and NS.n <= REGEXP_COUNT(js.json_text, '\\},') + 1
group by usr;
Notes:
1) do not name column "user" as it is reserved keyword
2) add as many dummy rows in NS subquery as there is maximum of json provider records
3) Yes, I know, this isn't very readable SQL :D

Passing multiple values for a single parameter from SSRS to hive

Creating a concatenated string in SSRS with values enclosed in single quotes
Any answers to the above question?. I am struck with the same problem:
The query from SSRS side is:
select *
from xyz.test_table1
where f1 in (?)
Datasource for me in this case is a hive table. User selection on the parameter is a multivalued parameter which is what I expect to be substituted as:
where in ('value1','value2')
when query is executed. But when looked at the query execution on the hive side, it comes as:
where in ('value1,value2')
How could I solve this?
From the documentation here, it seems Hive Query Language supports Common Table Expressions.
Consequently, something similar to the following should work:
declare #str nvarchar(4000) = ?; -- String to split.
with n(n) as (select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1)
-- Select the same number of rows as characters in #str as incremental row numbers.
-- Cross joins increase exponentially to a max possible 10,000 rows to cover largest #str length.
,t(t) as (select top (select len(isnull(#str,'')) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
-- Return the position of every value that follows the specified delimiter.
,s(s) as (select 1 union all select t+1 from t where substring(isnull(#str,''),t,1) = ',')
-- Return the start and length of every value, to use in the SUBSTRING function.
-- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
,l(s,l) as (select s,isnull(nullif(charindex(',',isnull(#str,''),s),0)-s,4000) from s)
-- Return each individual value in the delimited string along with it's position.
,v as (select row_number() over(order by s) as rn
,substring(#str,s,l) as item
from l
)
select *
from v
join xyz.test_table1 as t
on v.v = t.f1
If you rather understandably don't want this rigamarole in all of your datasets, you would need to encapsulate this logic into whatever the Hive equivalent of a SQL Server table-valued parameter is, perhaps a UDTF?
In SQL Server, the function would be defined as follows:
create function [dbo].[fn_StringSplit4k]
(
#str nvarchar(4000) = ' ' -- String to split.
,#delimiter as nvarchar(1) = ',' -- Delimiting value to split on.
,#num as int = null -- Which value to return.
)
returns table
as
return
-- Start tally table with 10 rows.
with n(n) as (select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1)
-- Select the same number of rows as characters in #str as incremental row numbers.
-- Cross joins increase exponentially to a max possible 10,000 rows to cover largest #str length.
,t(t) as (select top (select len(isnull(#str,'')) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
-- Return the position of every value that follows the specified delimiter.
,s(s) as (select 1 union all select t+1 from t where substring(isnull(#str,''),t,1) = #delimiter)
-- Return the start and length of every value, to use in the SUBSTRING function.
-- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
,l(s,l) as (select s,isnull(nullif(charindex(#delimiter,isnull(#str,''),s),0)-s,4000) from s)
select rn
,item
from(select row_number() over(order by s) as rn
,substring(#str,s,l) as item
from l
) a
where rn = #num
or #num is null;
Figured it out! Posting the answer for other users.
Provide the query(under Query in SSRS) as an expression like below:
="select * from xyz.test_table1 where f1 in ('"&Join(Parameters!param.Value,"','")&"')"
The above string manipulation translates to:
select * from xyz.test_table1 where f1 in ('value1','value2')
Note: value1, value2 here are the values from user selected multivalue parameter

Counting word occurrences in a table column

I have a table with a varchar(255) field. I want to get (via a query, function, or SP) the number of occurences of each word in a group of rows from this table.
If there are 2 rows with these fields:
"I like to eat bananas"
"I don't like to eat like a monkey"
I want to get
word | count()
---------------
like 3
eat 2
to 2
i 2
a 1
Any idea? I am using MySQL 5.2.
#Elad Meidar, I like your question and I found a solution:
SELECT SUM(total_count) as total, value
FROM (
SELECT count(*) AS total_count, REPLACE(REPLACE(REPLACE(x.value,'?',''),'.',''),'!','') as value
FROM (
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(t.sentence, ' ', n.n), ' ', -1) value
FROM table_name t CROSS JOIN
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a
,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
ORDER BY n
) n
WHERE n.n <= 1 + (LENGTH(t.sentence) - LENGTH(REPLACE(t.sentence, ' ', '')))
ORDER BY value
) AS x
GROUP BY x.value
) AS y
GROUP BY value
Here is the full working fiddle: http://sqlfiddle.com/#!2/17481a/1
First we do a query to extract all words as explained here by #peterm(follow his instructions if you want to customize the total number of words processed). Then we convert that into a sub-query and then we COUNT and GROUP BY the value of each word, and then make another query on top of that to GROUP BY not grouped words cases where accompanied signs might be present. ie: hello = hello! with a REPLACE
I would recommend not to do this in SQL at all. You're loading DB with something that it isn't best at. Selecting a group of rows and doing frequency calculation on the application side will be easier to implement, will work faster and will be maintained with less issues/headaches.
You can try this perverted-a-little way:
SELECT
(LENGTH(field) - LENGTH(REPLACE(field, 'word', ''))) / LENGTH('word') AS `count`
ORDER BY `count` DESC
This query can be very slow. Also, it looks pretty ugly.
I think you should do it like indexing, with additional table.
Whenever u create, update, or delete a row in your original table, you should update your indexing table. That indexing table should have the columns: word, and the number of occurrences.
I think you are trying to do too much with SQL if all the words are in one field of each row. I recommend to do any text processing/counting with your application after you grab the text fields from the db.