Print Prime Numbers with SQL query - mysql

I am new to StackOverflow and have got stuck with a query to print prime numbers from 2 to 1000.
I have used the below query need input if this is the most efficient way to code it.
WITH NUM AS (
SELECT LEVEL N
FROM DUAL CONNECT BY LEVEL <= 1000
)
SELECT LISTAGG(B.N,'-') WITHIN GROUP(ORDER BY B.N) AS PRIMES
FROM (
SELECT N,
CASE WHEN EXISTS (
SELECT NULL
FROM NUM N_INNER
WHERE N_INNER .N > 1
AND N_INNER.N < NUM.N
AND MOD(NUM.N, N_INNER.N)=0
) THEN
'NO PRIME'
ELSE
'PRIME'
END IS_PRIME
FROM NUM
) B
WHERE B.IS_PRIME='PRIME'
AND B.N!=1;
I know this question has been asked multiple times and I am requesting better solution if any. More over need input on how this works with MySQL/MS SQL/PostgreSQL.
Any help will make my understanding better.

In PostgreSQL probably the most fastest query that prints prime numbers up to 1000 is:
SELECT regexp_split_to_table('2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997',E',')::int
AS x
;
It took only 16 ms on my computer.
Note: a list of prime numbers was copied from https://en.wikipedia.org/wiki/Prime_number
and pasted into this long string
If you prefer SQL, then this works
WITH x AS (
SELECT * FROM generate_series( 2, 1000 ) x
)
SELECT x.x
FROM x
WHERE NOT EXISTS (
SELECT 1 FROM x y
WHERE x.x > y.x AND x.x % y.x = 0
)
;
It's two times slower - 31 ms.
Ans an equivalent version for Oracle:
WITH x AS(
SELECT level+1 x
FROM dual
CONNECT BY LEVEL <= 999
)
SELECT x.x
FROM x
WHERE NOT EXISTS (
SELECT 1 FROM x y
WHERE x.x > y.x AND remainder( x.x, y.x) = 0
)
;

The most obvious improvement is that instead of checking from 1 to n you can check from 1 to the square root of n.
A second major optimization would be to use a temporary table to store the results and check them first. This way you can iterate incrementally from 1 to n, and only check the known primes from 1 to square root of n (recursively doing that until you have a list). If you go about things this way you would probably want to set up the prime detection in a function and then do the same with your number series generator.
That second one though means extending SQL and so I don't know if that fits your requirements.
For postgresql I would use generate_series go generate the list of numbers. I would then create functions which would then either store the list of primes in a temporary table or pass them back in and out in an ordered array and then couple them like that

MariaDB (with sequence plugin)
Similar to kordirkos algorithm:
select 2 as p union all
select n.seq
from seq_3_to_1000_step_2 n
where not exists (
select 1
from seq_3_to_32_step_2 q
where q.seq < n.seq
and n.seq mod q.seq = 0
);
Using LEFT JOIN:
select 2 as p union all
select n.seq
from seq_3_to_1000_step_2 n
left join seq_3_to_32_step_2 q
on q.seq < n.seq
and n.seq mod q.seq = 0
where q.seq is null;
MySQL
There are no sequence generating helpers in MySQL. So the sequence tables have to be created first:
drop temporary table if exists n;
create temporary table if not exists n engine=memory
select t2.c*100 + t1.c*10 + t0.c + 1 as seq from
(select 0 c union all select 1 c union all select 2 c union all select 3 c union all select 4 c union all select 5 c union all select 6 c union all select 7 c union all select 8 c union all select 9 c) t0,
(select 0 c union all select 1 c union all select 2 c union all select 3 c union all select 4 c union all select 5 c union all select 6 c union all select 7 c union all select 8 c union all select 9 c) t1,
(select 0 c union all select 1 c union all select 2 c union all select 3 c union all select 4 c union all select 5 c union all select 6 c union all select 7 c union all select 8 c union all select 9 c) t2
having seq > 2 and seq % 2 != 0;
drop temporary table if exists q;
create temporary table if not exists q engine=memory
select *
from n
where seq <= 32;
alter table q add primary key seq (seq);
Now similar queries can be used:
select 2 as p union all
select n.seq
from n
where not exists (
select 1
from q
where q.seq < n.seq
and n.seq mod q.seq = 0
);
select 2 as p union all
select n.seq
from n
left join q
on q.seq < n.seq
and n.seq mod q.seq = 0
where q.seq is null;
sqlfiddle

Oracle and without inner select in getting part:
with tmp(id)
as (
select level id from dual connect by level <= 100
) select t1.id from tmp t1
JOIN tmp t2
on MOD(t1.id, t2.id) = 0
group by t1.ID
having count(t1.id) = 2
order by t1.ID
;

/* Below is my solution */
/* Step 1: Get all the numbers till 1000 */
with tempa as
(
select level as Num
from dual
connect by level<=1000
),
/* Step 2: Get the Numbers for finding out the factors */
tempb as
(
select a.NUm,b.Num as Num_1
from tempa a , tempa b
where b.Num<=a.Num
),
/*Step 3:If a number has exactly 2 factors, then it is a prime number */
tempc as
(
select Num, sum(case when mod(num,num_1)=0 then 1 end) as Factor_COunt
from tempb
group by Num
)
select listagg(Num,'&') within group (order by Num)
from tempc
where Factor_COunt=2
;

Tested on sqlite3
WITH nums(n) AS
(
SELECT 1
UNION ALL
SELECT n + 1 FROM nums WHERE n < 100
)
SELECT n
FROM (
SELECT n FROM nums
)
WHERE n NOT IN (
SELECT n
FROM nums
JOIN ( SELECT n AS n2 FROM nums )
WHERE n <> 1
AND n2 <> 1
AND n <> n2
AND n2 < n
AND n % n2 = 0
ORDER BY n
)
AND n <> 1
Tested on Vertica 8
WITH seq AS (
SELECT ROW_NUMBER() OVER() AS n
FROM (
SELECT 1
FROM (
SELECT date(0) + INTERVAL '1 second' AS i
UNION ALL
SELECT date(0) + INTERVAL '100 seconds' AS i
) _
TIMESERIES tm AS '1 second' OVER(ORDER BY i)
) _
)
SELECT n
FROM (SELECT n FROM seq) _
WHERE n NOT IN (
SELECT n FROM (
SELECT s1.n AS n, s2.n AS n2
FROM seq AS s1
CROSS JOIN seq AS s2
ORDER BY n, n2
) _
WHERE n <> 1
AND n2 <> 1
AND n <> n2
AND n2 < n
AND n % n2 = 0
)
AND n <> 1
ORDER BY n

This is what worked for me in the SQL server. I tried to reduce the order of my nested loops.
declare #var int
declare #i int
declare #result varchar (max)
set #var = 1
select #result = '2&3&5' --first few obvious prime numbers
while #var < 1000 --the first loop
begin
set #i = 3;
while #i <= #var/2 --the second loop which I attempted to reduce the order
begin
if #var%#i = 0
break;
if #i=#var/2
begin
set #result = #result + '&' + CAST(#var AS VARCHAR)
break;
end
else
set #i = #i + 1
end
set #var = #var + 1;
end
print #result

SELECT LISTAGG(PRIME_NUMBER,'&') WITHIN GROUP (ORDER BY PRIME_NUMBER)
FROM
(
SELECT L PRIME_NUMBER FROM
(
SELECT LEVEL L FROM DUAL CONNECT BY LEVEL <= 1000 ),
(
SELECT LEVEL M FROM DUAL CONNECT BY LEVEL <= 1000
) WHERE M <= L
GROUP BY L
HAVING COUNT(CASE WHEN L/M = TRUNC(L/M) THEN 'Y' END
) = 2
ORDER BY L
);

SELECT GROUP_CONCAT(NUMB SEPARATOR '&')
FROM (
SELECT #num:=#num+1 as NUMB FROM
information_schema.tables t1,
information_schema.tables t2,
(SELECT #num:=1) tmp
) tempNum
WHERE NUMB<=1000 AND NOT EXISTS(
SELECT * FROM (
SELECT #nu:=#nu+1 as NUMA FROM
information_schema.tables t1,
information_schema.tables t2,
(SELECT #nu:=1) tmp1
LIMIT 1000
) tatata
WHERE FLOOR(NUMB/NUMA)=(NUMB/NUMA) AND NUMA<NUMB AND NUMA>1
)

MySQL Code :
DECLARE
#i INT,
#a INT,
#count INT,
#p nvarchar(max)
SET #i = 1
WHILE (#i <= 1000)
BEGIN SET #count = 0
SET #a = 1
WHILE (#a <= #i)
BEGIN IF (#i % #a = 0) SET #count = #count + 1 SET #a = #a + 1
END IF (#count = 2) SET #P = CONCAT(#P,CONCAT(#i,'&')) SET #i = #i + 1
END
PRINT LEFT(#P, LEN(#P) - 1)

The below code works to find prime numbers in SQL
Tested on SampleDB of local server
CREATE procedure sp_PrimeNumber(#number int)
as
begin
declare #i int
declare #j int
declare #isPrime int
set #isPrime=1
set #i=2
set #j=2
while(#i<=#number)
begin
while(#j<=#number)
begin
if((#i<>#j) and (#i%#j=0))
begin
set #isPrime=0
break
end
else
begin
set #j=#j+1
end
end
if(#isPrime=1)
begin
SELECT #i
end
set #isPrime=1
set #i=#i+1
set #j=2
end
end
I have created the stored procedure which has a parameter #number to find the prime numbers up to that given number
In order to get the prime numbers we can execute the below stored procedure
EXECUTE sp_PrimeNumber 100 -- gives prime numbers up to 100
If you are new to stored procedures and want to find the prime numbers in SQL we can use the below code
Tested on master DB
declare #i int
declare #j int
declare #isPrime int
set #isPrime=1
set #i=2
set #j=2
while(#i<=100)
begin
while(#j<=100)
begin
if((#i<>#j) and (#i%#j=0))
begin
set #isPrime=0
break
end
else
begin
set #j=#j+1
end
end
if(#isPrime=1)
begin
SELECT #i
end
set #isPrime=1
set #i=#i+1
set #j=2
end
This code can give the prime numbers between 1 to 100. If we want to find more prime numbers edit the #i and #j arguments in the while loop and execute

Simple query in PostgreSQL:
SELECT serA.el AS prime
FROM generate_series(2, 100) serA(el)
LEFT JOIN generate_series(2, 100) serB(el) ON serA.el >= POWER(serB.el, 2)
AND serA.el % serB.el = 0
WHERE serB.el IS NULL
Enjoy! :)

For SQL Server We can use below CTE
SET NOCOUNT ON
;WITH Prim AS
(
SELECT 2 AS Value
UNION ALL
SELECT t.Value+1 AS VAlue
FROM Prim t
WHERE t.Value < 1000
)SELECT *
FROM Prim t
WHERE NOT EXISTS( SELECT 1 FROM prim t2
WHERE t.Value % t2.Value = 0
AND t.Value != t2. Value)
OPTION (MAXRECURSION 0)

One simple one can be like this
select level id1 from dual connect by level < 2001
minus
select distinct id1 from (select level id1 from dual connect by level < 46) t1 inner join (select level id2 from dual connect by level < 11) t2
on 1=1 where t1.id1> t2.id2 and mod(id1,id2)=0 and id2<>1

Simplest method For SQL Server
DECLARE #range int = 1000, #x INT = 2, #y INT = 2
While (#y <= #range)
BEGIN
while (#x <= #y)
begin
IF ((#y%#x) =0)
BEGIN
IF (#x = #y)
PRINT #y
break
END
IF ((#y%#x)<>0)
set #x = #x+1
end
set #x = 2
set #y = #y+1
end

MySQL QUERY SOLUTION
I have solved this problem in mysql which is following:
SET #range = 1000;
SELECT GROUP_CONCAT(R2.n SEPARATOR '&')
FROM (
SELECT #ctr2:=#ctr2+1 "n"
FROM information_schema.tables R2IS1,
information_schema.tables R2IS2,
(SELECT #ctr2:=1) TI
WHERE #ctr2<#range
) R2
WHERE NOT EXISTS (
SELECT R1.n
FROM (
SELECT #ctr1:=#ctr1+1 "n"
FROM information_schema.tables R1IS1,
information_schema.tables R1IS2,
(SELECT #ctr1:=1) I1
WHERE #ctr1<#range
) R1
WHERE R2.n%R1.n=0 AND R2.n>R1.n
)
Note: No. of information_schema.tables should be increased for more range e.g. if range is 100000 so set the info tables by checking yourself.

--Create Table prime_number_t
create table prime_number_t (
integervalue_c integer not null primary key
);
--Insert Data into table prime_number_t
INSERT ALL
into prime_number_t(integervalue_c) values (1)
into prime_number_t(integervalue_c) values (2)
into prime_number_t(integervalue_c) values (3)
into prime_number_t(integervalue_c) values (4)
into prime_number_t(integervalue_c) values (5)
into prime_number_t(integervalue_c) values (6)
into prime_number_t(integervalue_c) values (7)
into prime_number_t(integervalue_c) values (8)
into prime_number_t(integervalue_c) values (9)
into prime_number_t(integervalue_c) values (10)
SELECT 1 FROM DUAL;
COMMIT;
--Write an SQL statement to determine which of the below numbers are prime numbers
--same query works for REMAINDER function also instead of MOD function
WITH cte_prime_number_t AS
(
select integervalue_c
from prime_number_t
order by integervalue_c
),
cte_maxval AS
(
select max(integervalue_c) AS maxval FROM cte_prime_number_t
),
cte_level AS
(
select LEVEL+1 as lvl
from dual,
cte_maxval
CONNECT BY LEVEL <= cte_maxval.maxval
)
SELECT DISTINCT cpnt.integervalue_c as PrimeNumbers
FROM cte_prime_number_t cpnt
inner join cte_level cl on lvl <= (SELECT maxval FROM cte_maxval)
WHERE NOT EXISTS (
SELECT 1 FROM cte_level cpn
WHERE cpnt.integervalue_c > cpn.lvl AND mod(cpnt.integervalue_c,cpn.lvl) = 0
)
order by PrimeNumbers;

For MySQL 8 or above
/* create a table with one row and that starts with 2 ends at 1000*/
SET cte_max_recursion_depth = 1001; /* works for MySQL 8.0*/
;WITH RECURSIVE sequence AS (
SELECT 1 AS l
UNION ALL
SELECT l + 1 AS value
FROM sequence
WHERE sequence.l < 1000
),
/* create a caretesian product of a number to other numbers uptil this very number
so for example if there is a value 5 in a row then it creates these rows using the table below
(5,2), (5,3), (5,4), (5,5) */
J as (
SELECT (a.l) as m , (b.l) as n
FROM sequence a, sequence b
WHERE b.l <= a.l)
,
/*take a row from column 1 then divide it with other column values but group by column 1 first,
note the completely divisible count*/
f as
( SELECT m , SUM(CASE WHEN mod(m,n) = 0 THEN 1 END) as fact
FROM J
GROUP BY m
HAVING fact = 2
ORDER BY m ASC /*this view return numbers in descending order so had to use order by*/
)
/* this is for string formatting, converting a column to a string with separator &*/
SELECT group_concat(m SEPARATOR '&') FROM f;

This worked for me in MySql:
select '2&3&5&7&11&13&17&19&23&29&31&37&41&43&47&53&59&61&67&71&73&79&83&89&97&101&103&107&109&113&127&131&137&139&149&151&157&163&167&173&179&181&191&193&197&199&211&223&227&229&233&239&241&251&257&263&269&271&277&281&283&293&307&311&313&317&331&337&347&349&353&359&367&373&379&383&389&397&401&409&419&421&431&433&439&443&449&457&461&463&467&479&487&491&499&503&509&521&523&541&547&557&563&569&571&577&587&593&599&601&607&613&617&619&631&641&643&647&653&659&661&673&677&683&691&701&709&719&727&733&739&743&751&757&761&769&773&787&797&809&811&821&823&827&829&839&853&857&859&863&877&881&883&887&907&911&919&929&937&941&947&953&967&971&977&983&991&997';
Well, I know the above one is just hardcoded and you will be able to run the problem but it's not what we should go for as a programmer so here is my solution for oracle:
SELECT LISTAGG(L1,'&') WITHIN GROUP (ORDER BY L1) FROM (Select L1 FROM (SELECT LEVEL L1 FROM DUAL CONNECT BY LEVEL<=1000) Where L1 <> 1 MINUS select L1 from (SELECT LEVEL L1 FROM DUAL CONNECT BY LEVEL<=1000) A , (SELECT LEVEL L2 FROM DUAL CONNECT BY LEVEL<=1000) B Where L2<=L1 and MOD(L1,L2)=0 AND L1<>L2 AND L2<>1);

Worked in Oracle:
SELECT LISTAGG(a,'&')
WITHIN GROUP (ORDER BY a)
FROM(WITH x AS(
SELECT level+1 x
FROM dual
CONNECT BY LEVEL <= 999
)
SELECT x.x as a
FROM x
WHERE NOT EXISTS (
SELECT 1 FROM x y
WHERE x.x > y.x AND remainder( x.x, y.x) = 0
));

SELECT GROUP_CONCAT(distinct PRIME_NUMBER SEPARATOR '&')
FROM (SELECT #prime:=#prime + 1 AS PRIME_NUMBER
FROM information_schema.tables
CROSS JOIN (SELECT #prime:=1) r
WHERE #num <1000
) p
WHERE NOT EXISTS (
SELECT * FROM
(SELECT #divisor := #divisor + 1 AS DIVISOR FROM
information_schema.tables
CROSS JOIN (SELECT #divisor:=1) r1
WHERE #divisor <=1000
) d
WHERE MOD(PRIME_NUMBER, DIVISOR) = 0 AND PRIME_NUMBER != DIVISOR) ;
enter code here
Explanation:
The two inner SELECTs (SELECT #prime and SELECT #divisor) create two lists. Both of them contain numbers from 1 to 1000. The first list is the "list of potential primes" and the second is the "list of divisors"
Then, we iterate over the list of the potential primes (the outer SELECT), and for each number from this list we look for divisors (SELECT * FROM clause) that can divide the number without a reminder and are not equal to the number (WHERE MOD... clause). If at least one such divisor exists, the number is not prime and is not selected (WHERE NOT EXISTS... clause).

Related

Setting and using a variable in a MySQL WHERE

If I do the following query on MySQL 5.7.16, then the resultset contains one row with the value 2, which is expected:
SELECT *
FROM (SELECT 1 as x UNION SELECT 2 UNION SELECT 3) AS t
WHERE ( 2 IS NULL OR t.x = 2 )
;
-- Resultset: 1 row, x = 2
Now, I would like to use this logic in a prepared statement, like ? IS NULL OR t.x = ?. So you see that the same parameter appears twice. Hence, I applied an advice I found on SO (I don't remember the exact location): put the parameter in a MySQL session variable, and use it:
SELECT *
FROM (SELECT 1 as x UNION SELECT 2 UNION SELECT 3) AS t
WHERE ( (#x := 2) IS NULL OR t.x = #x )
;
-- Resultset: 0 row
But this fails: no row is returned. When I also select the #x variable, in order to see what's going on, I get a NULL value for #x:
SELECT #x
FROM (SELECT 1 as x UNION SELECT 2 UNION SELECT 3) AS t
WHERE ( (#x := 2) IS NULL OR t.x = 2 )
;
-- Resultset: 1 row, #x = NULL
So it seems that the variable is not set when put in the WHERE? What is going on?
I could make an INNER JOIN (SELECT 2 AS x) AS params and use params.x inside the WHERE, but I would like to understand what's happening in that WHERE ( (#x = 2) IS NULL OR t.x = #x ).
What is happening here is that the order of execution inside WHERE is arbitrary. This means that using
(#x := 2) IS NULL OR t.x = 2
there is no way to guarantee that #x := 2 will be executed first.
To correctly initialize the variable use a CROSS JOIN:
SELECT *
FROM (SELECT 1 as x UNION SELECT 2 UNION SELECT 3) AS t
CROSS JOIN (SELECT #x := 2) AS v
WHERE ( #x IS NULL OR t.x = #x )

Store values in different variables in SQL, separated by (Comma) ","

I need to separate values and store them in different variables in SQL,
for example
a='3100,3101,3102,....'
And the output should be
x=3100
y=3101
z=3102
.
.
.
create function [dbo].[udf_splitstring] (#tokens varchar(max),
#delimiter varchar(5))
returns #split table (
token varchar(200) not null )
as
begin
declare #list xml
select #list = cast('<a>'
+ replace(#tokens, #delimiter, '</a><a>')
+ '</a>' as xml)
insert into #split
(token)
select ltrim(t.value('.', 'varchar(200)')) as data
from #list.nodes('/a') as x(t)
return
end
GO
declare #cad varchar(100)='3100,3101,3102'
select *,ROW_NUMBER() over (order by token ) as rn from udf_splitstring(#cad,',')
token rn
3100 1
3101 2
3102 3
The results of the Parse TVF can easily be incorporated into a JOIN, or an IN
Declare #a varchar(max)='3100,3101,3102'
Select * from [dbo].[udf-Str-Parse](#a,',')
Returns
RetSeq RetVal
1 3100
2 3101
3 3102
The UDF if needed (much faster than recursive, loops, and xml)
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#Delimiter varchar(25))
Returns Table
As
Return (
with cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (IsNull(DataLength(#String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 a,cte1 b,cte1 c,cte1 d) A ),
cte3(N) As (Select 1 Union All Select t.N+DataLength(#Delimiter) From cte2 t Where Substring(#String,t.N,DataLength(#Delimiter)) = #Delimiter),
cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(#Delimiter,#String,s.N),0)-S.N,8000) From cte3 S)
Select RetSeq = Row_Number() over (Order By A.N)
,RetVal = LTrim(RTrim(Substring(#String, A.N, A.L)))
From cte4 A
);
--Orginal Source http://www.sqlservercentral.com/articles/Tally+Table/72993/
--Much faster than str-Parse, but limited to 8K
--Select * from [dbo].[udf-Str-Parse-8K]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse-8K]('John||Cappelletti||was||here','||')
I suggest you to use following query, it's much faster than other functions like cross apply and udf.
SELECT
Variables
,S_DATA
FROM (
SELECT
Variables
,CASE WHEN LEN(LIST2)>0 THEN LTRIM(RTRIM(SUBSTRING(LIST2, NUMBER+1, CHARINDEX(',', LIST2, NUMBER+1)-NUMBER - 1)))
ELSE NULL
END AS S_DATA
,NUMBER
FROM(
SELECT Variables
,','+COMMA_SEPARETED_COLUMN+',' LIST2
FROM Tb1
)DT
LEFT OUTER JOIN TB N ON (N.NUMBER < LEN(DT.LIST2)) OR (N.NUMBER=1 AND DT.LIST2 IS NULL)
WHERE SUBSTRING(LIST2, NUMBER, 1) = ',' OR LIST2 IS NULL
) DT2
WHERE S_DATA<>''
and also you should create a table 'NUMBER' before running the above query.
CREATE TABLE TB (Number INT)
DECLARE #I INT=0
WHILE #I<1000
BEGIN
INSERT INTO TB VALUES (#I)
SET #I=#I+1
END

How can match a string of comma separated , irrespective of their position order in Mysql

Problem:
I want to test a set of values is equal to another set but not necessary their order of position will be same.
For example:
'a,b,c,d' must be equal to 'b,a,c,d'
What I have tried:
I have tried IN Clause and I have checked with FIND_IN_SET.
SELECT 'a,b,c,d' IN 'b,c,a,d';
Both of them can not do this work.
Will be thankful if anyone can help.
Thanks
Sandeep
This demonstrates the use the splitting of values to multiple rows, mentioned by GolezTrol in combination with FIND_IN_SET, modified to function to be used in forms like:
SELECT are_sets_equal(col_with_set, 'a,b,d,c') FROM example;
or
SELECT * FROM example
WHERE are_sets_equal(col_with_set, 'a,b,d,c')
The idea is this:
Split the the first set to a temporary table
Check how many of those values are found in the second set.
If this count is equal to the count of elements in both sets, then the sets are equal
The function will return 1, if both sets are equal and 0, if the sets differ as by requirement.
The limit for both sets is 1000 values, but could be expanded easily:
DELIMITER //
CREATE FUNCTION are_sets_equal(set_a VARCHAR(2000), set_b VARCHAR(2000)) RETURNS BOOLEAN
BEGIN
DECLARE is_equal BOOLEAN;
DECLARE count_a INT;
DECLARE count_b INT;
-- calculate the count of elements in both sets
SET count_a = 1 + LENGTH(set_a) - LENGTH(REPLACE(set_a, ',', ''));
SET count_b = 1 + LENGTH(set_b) - LENGTH(REPLACE(set_b, ',', ''));
SELECT
-- if all elements of the first set are contained in the second
-- set and both sets have the same number of elements then both
-- sets are considered equal
COUNT(t.value) = count_a AND count_a = count_b INTO is_equal
FROM (
SELECT
SUBSTRING_INDEX(SUBSTRING_INDEX(e.col, ',', n.n), ',', -1) value
FROM ( SELECT set_a AS col ) e
CROSS JOIN(
-- build for up to 1000 separated values
SELECT
a.N + b.N * 10 + c.N * 100 + 1 AS 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
,(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) c
ORDER BY n
) n
WHERE n.n <= count_a
) t
WHERE FIND_IN_SET(t.value, set_b);
return is_equal;
END //
DELIMITER ;
Explanation
Building a numbers table
SELECT
a.N + b.N * 10 + c.N * 100 + 1 AS 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
,(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) c
ORDER BY n
builds a number table with the values from 1 to 1000 on the fly. How to expand this to a greater range should be obvious.
Note Such a numbers table could be contained in your database, so there would be no need to create one on the fly.
Split a set to a table
With the help of this number table we can split the value list to a table, using nested SUBSTRING_INDEX calls to cut just one value after the other from the list as mentioned in SQL split values to multiple rows:
SELECT
SUBSTRING_INDEX(SUBSTRING_INDEX(t.col, ',', n.n), ',', -1) value
FROM (SELECT #set_a as col ) t CROSS JOIN (
-- build for up to 100 separated values
SELECT
a.N + b.N * 10 + c.N * 100 + 1 AS 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
,(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) c
ORDER BY n
) n
WHERE
n <= 1 + LENGTH(#set_a) - LENGTH(REPLACE(#set_a, ',', ''))
Count the elements of the sets
We get the count of elements in the list by the expression in the WHERE clause: we have one more values than occurences of the separator.
Then we restrict the result by searching those values in the second set with FIND_IN_SET.
As a last step we check count of values in the result against the count of values in both sets and return this value.
Demo
Experiment with this demo.
FIND_IN_SET should do the trick, but the first value is an individual value and doesn't work right if it contains a comma. You would have to look for each individual value:
SELECT
FIND_IN_SET('a', 'b,c,a,d') AND
FIND_IN_SET('b', 'b,c,a,d') AND
FIND_IN_SET('c', 'b,c,a,d') AND
FIND_IN_SET('d', 'b,c,a,d')
If you don't have these separate values available, maybe you can split the input value into multiple values. The answers to the question 'Split values to multiple rows' might give you some inspiration.
The better solution would be not to store comma separated values at all. It's considered bad practice.
You can use a UDF(user defined function) to compare your sets,As from comments no duplicate values will be in the sets,I have little customized a UDF function provided by #Simon at mso.net.I have calculated a count of values in a second comma separated list and in the end compared with the matched result of find_in_set stored in numReturn variable,If both equal the return 1 else return 0 for non matched.Please not this will not work for repeated/duplicate values in a set
DELIMITER $$
DROP FUNCTION IF EXISTS `countMatchingElements`$$
CREATE DEFINER = `root` #`localhost` FUNCTION `countMatchingElements` (
inFirstList VARCHAR (1000),
inSecondList VARCHAR (1000)
) RETURNS TINYINT (3) UNSIGNED NO SQL DETERMINISTIC SQL SECURITY INVOKER
BEGIN
DECLARE numReturn TINYINT UNSIGNED DEFAULT 0 ;
DECLARE idsInFirstList TINYINT UNSIGNED ;
DECLARE currentListItem VARCHAR (255) DEFAULT '' ;
DECLARE currentID TINYINT UNSIGNED ;
DECLARE total_values_in_second INT DEFAULT 0 ;
SET total_values_in_second = ROUND(
(
LENGTH(inSecondList) - LENGTH(REPLACE (inSecondList, ',', ''))
) / LENGTH(',')
) + 1 ;
SET idsInFirstList = (CHAR_LENGTH(inFirstList) + 1) - CHAR_LENGTH(REPLACE(inFirstList, ',', '')) ;
SET currentID = 1 ;
-- Loop over inFirstList, and for each element that is in inSecondList increment numReturn
firstListLoop :
REPEAT
SET currentListItem = SUBSTRING_INDEX(
SUBSTRING_INDEX(inFirstList, ',', currentID),
',',
- 1
) ;
IF FIND_IN_SET(currentListItem, inSecondList)
THEN SET numReturn = numReturn + 1 ;
END IF ;
SET currentID = currentID + 1 ;
UNTIL currentID > idsInFirstList
END REPEAT firstListLoop ;
IF total_values_in_second = numReturn
THEN RETURN 1 ;
ELSE RETURN 0 ;
END IF ;
END $$
DELIMITER ;
Fiddle Demo

I tried this code in mysql but it was row operation,how to insert column wise data

MySQL stored procedures can be used to split the string, the following details for your usage of MySQL stored procedures, for your reference learning purposes.
Existing string, such as Apple, banana, orange, pears, grape, it should follow the comma (,) is divided into:
apple
banana
orange
pears
grape
Where in () method can then query.
1, the specific function:
Function: func_split_TotalLength the
DELIMITER $ $
DROP function IF EXISTS `func_split_TotalLength` $ $
CREATE DEFINER = `root` # `%` FUNCTION `func_split_TotalLength`
(F_string varchar (1000), f_delimiter varchar (5)) RETURNS int (11)
BEGIN
return 1 + (length (f_string) - length (replace (f_string, f_delimiter,'')));
END $ $
DELIMITER;
Function: func_split
DELIMITER $ $
DROP function IF EXISTS `func_split` $ $
CREATE DEFINER = `root` # `%` FUNCTION `func_split`
(F_string varchar (1000), f_delimiter varchar (5), f_order int) RETURNS varchar (255) CHARSET utf8
BEGIN
declare result varchar (255) default '';
set result = reverse (substring_index (reverse (substring_index (f_string, f_delimiter, f_order)), f_delimiter, 1));
return result;
END $ $
DELIMITER;
Stored procedure: SplitString
DELIMITER $ $
DROP PROCEDURE IF EXISTS `splitString` $ $
CREATE Procedure the `SplitString`
(IN f_string varchar (1000), IN f_delimiter varchar (5))
BEGIN
declare cnt int default 0;
declare i int default 0;
set cnt = func_split_TotalLength (f_string, f_delimiter);
DROP TABLE IF EXISTS `tmp_split`;
create temporary table `tmp_split` (`status` varchar (128) not null) DEFAULT CHARSET = utf8;
while i <cnt
do
set i = i + 1;
insert into tmp_split (`status`) values ??(func_split (f_string, f_delimiter, i));
end while;
END $ $
DELIMITER;
2, the test will be successful segmentation
call splitString ("apple, banana, orange, pears, grape", ",");
select * from tmp_split;
The results are splitting success:
mysql> call splitString ("apple, banana, orange, pears, grape", ",");
select * from tmp_split;
Query OK, 1 row affected but i need to insert by column wise data...,please help me
Well I would use an other approach to this problem, to get the result in table form:
Simpler approach without a stored procedure
Assuming you have a table example with two columns: id and col1 like so:
CREATE TABLE example (
id INT NOT NULL PRIMARY KEY,
col1 VARCHAR(1000)
);
and those values
id | col
-----------------------------
1 | abcd,efgh,ijkl,ghjy,sdfg
2 | some other text
and you want to get the comma separated values from col into a column with one value at the time for the id = 1. Then I would use following sql:
SELECT
id,
SUBSTRING_INDEX(SUBSTRING_INDEX(e.col, ',', t.n), ',', -1) value
FROM example e CROSS JOIN (
SELECT
1 + a.N + b.N * 10 + c.N * 100 AS 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
,(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) c
ORDER BY n
) t
WHERE
e.id = 1
AND
t.n <= (1 + LENGTH(e.col) - LENGTH(REPLACE(e.col, ',', '')));
It's the same procedure as in the stored procedure. You can use this result easily in joins or other operations.
See this Demo
First part assuming, a stored procedure was needed.
stored procedure
no other function needed
no explicit temp table
works up to 1000 items, easily exptensible
Code:
DROP procedure splitString;
DELIMITER $ $
CREATE procedure splitString(IN f_string VARCHAR(1000), IN f_delimiter VARCHAR(5))
BEGIN
SELECT SUBSTRING_INDEX(
SUBSTRING_INDEX(h.haystack, f_delimiter, t.n
), f_delimiter, -1) as value
FROM (SELECT f_string as haystack) h
CROSS JOIN (
SELECT
1 + a.N + b.N * 10 + c.N * 100 AS 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
,(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) c
ORDER BY n
) t
WHERE t.n <=
(
1
+ (
CHAR_LENGTH(f_string)
-
CHAR_LENGTH(REPLACE(f_string, f_delimiter, ''))
)
/ CHAR_LENGTH(f_delimiter)
);
END;
$ $
DELIMITER ;
Explanation
1. Generating a list of numbers from 1 to 1000
The most inner SELECT
SELECT
1 + a.N + b.N * 10 + c.N * 100 AS 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
,(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) c
ORDER BY n
does not more than creating a list with the numbers from 1 to 1000. If you need more then you should see, how it's done. Add a line with those union all and call this d, add + d.N * 1000 to the sum.
2. Performing the split and getting just one element per row
We use this list to do the split of your func_split in the form of (a bit more readable statements)
SELECT SUBSTRING_INDEX(
SUBSTRING_INDEX(h.haystack, f_delimiter, 1
), f_delimiter, -1) as value
FROM (SELECT f_string as haystack) h
SELECT SUBSTRING_INDEX(
SUBSTRING_INDEX(h.haystack, f_delimiter, 2
), f_delimiter, -1) as value
FROM (SELECT f_string as haystack) h
SELECT SUBSTRING_INDEX(
SUBSTRING_INDEX(h.haystack, f_delimiter, 3
), f_delimiter, -1) as value
FROM (SELECT f_string as haystack) h
that extract the first, second, third, ... substring, separated with the f_delimiter.
Getting the number of elements
Our WHERE clause is all but self-explanatory:
-- because we begin by 1 we've got to include the upper limit
WHERE t.n <=
(
1 -- we have one comma less than parts
+ (
-- count how many characters (not bytes) we lose by the replace operation
CHAR_LENGTH(f_string)
-
CHAR_LENGTH(REPLACE(f_string, f_delimiter, ''))
)
-- and divide this by the count of characters in the delimiter string
/ CHAR_LENGTH(f_delimiter)
);
Note
LENGTH() instead of CHAR_LENGTH() would do too, because the result of the division would be the same, but I like to do it right :-)
** Check of function**
You can check the result with
CALL splitString('apple, bananas, grape, ananas, pears', ', ');
it returns
value
-----
apple
bananas
grape
ananas
pears

How to remove all the non numeric characters from column in mysql in bulk data

I want to remove all the non numeric characters from the column. I have bulk data in my database.
Currently I am using method as describe in below link:
http://venerableagents.wordpress.com/2011/01/29/mysql-numeric-functions/
The problem is that its taking too much time for preocessing.
For 1 million of row current logic takes 1 hour to process the data.
please help me..
Thank You,
Ronak
I assume you're doing something like:
update myTable set foo = NumericOnly(foo);
I don't know how much better you can do than that.
One thing that might help a bit, though. In that NumericOnly function, they're doing extra work. I'd remove the SET idx = LENGTH(val)+1; line, since all that will do is start checking the end of the string (the parts we've already checked) again. A string with 5 leading non-numerics would be checked, in full, 5 times.
Removing the line would leave:
DROP FUNCTION IF EXISTS NumericOnly;
CREATE FUNCTION NumericOnly (val VARCHAR(255))
RETURNS VARCHAR(255)
BEGIN
DECLARE idx INT DEFAULT 0;
IF ISNULL(val) THEN RETURN NULL; END IF;
IF LENGTH(val) = 0 THEN RETURN ""; END IF;
SET idx = LENGTH(val);
WHILE idx > 0 DO
IF IsNumeric(SUBSTRING(val,idx,1)) = 0 THEN
SET val = REPLACE(val,SUBSTRING(val,idx,1),"");
END IF;
SET idx = idx - 1;
END WHILE;
RETURN val;
END;
Here's another spin on things...
DEMO: http://sqlfiddle.com/#!2/0c96e/21
First, create yourself a numbers table
CREATE TABLE numbers (
number int NOT NULL PRIMARY KEY
);
INSERT INTO numbers (number)
SELECT n0 + n1 + n2 + n3 + n4 + n5
FROM (SELECT 0 AS n0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) AS z0
CROSS
JOIN (SELECT 0 AS n1 UNION SELECT 4 UNION SELECT 8 UNION SELECT 12) AS z1
CROSS
JOIN (SELECT 0 AS n2 UNION SELECT 16 UNION SELECT 32 UNION SELECT 48) AS z2
CROSS
JOIN (SELECT 0 AS n3 UNION SELECT 64 UNION SELECT 128 UNION SELECT 192) AS z3
CROSS
JOIN (SELECT 0 AS n4 UNION SELECT 256 UNION SELECT 512 UNION SELECT 768) AS z4
CROSS
JOIN (SELECT 0 AS n5 UNION SELECT 1024 UNION SELECT 2048 UNION SELECT 3072) AS z5
ORDER
BY 1;
Here's some sample data to play with
CREATE TABLE your_table (
foo varchar(50)
);
INSERT INTO your_table (foo)
VALUES ('124nhasfonasf13')
, ('NONE')
, ('r937')
, ('o9o9')
, ('n444n4n455n')
, ('blah');
Then here's a query to give you just the numbers. Should be more efficient as it is SET based instead of iterative like your function example...
SELECT foo
, Group_Concat(c ORDER BY position SEPARATOR '')
FROM (
SELECT vals.foo
, numbers.number As position
, SubString(vals.foo, numbers.number, 1) As c
FROM (
SELECT foo
, Length(foo) As lngth
FROM your_table
WHERE foo REGEXP '[0-9]'
) As vals
INNER
JOIN numbers
ON numbers.number BETWEEN 1 AND vals.lngth
) As x
WHERE c REGEXP '[0-9]'
GROUP
BY foo