I need to create a table function that produces a parameter up to a specified number in column 1 always starting from 1. In column 2, if column 1 is divisible by 5 it will say 'Div5' otherwise NULL.
So as an example. I specify column 1 will stop at 5 the end result will look as follows;
1 NULL
2 NULL
3 NULL
4 NULL
5 Div5
I can create the function, but I'm not sure how to create the conditional first column, or how to say if column 2 divided by 5 is an integer then 'Div5' if it's a decimal then NULL;
create function MyFunction ()
Returns #Division Table
(Ind int ,
Div5 varchar(30))
AS
begin
Insert Into #Division (Ind, Div5)
select ???,???
Return;
End;
I hope this gives enough detail?
Thank you :)
This should do the trick:
DECLARE #divisor INT = 10, #limit INT = 100;
WITH
L0 AS(SELECT 1 AS C UNION ALL SELECT 1 AS O),
L1 AS(SELECT 1 AS C FROM L0 AS A CROSS JOIN L0 AS B),
L2 AS(SELECT 1 AS C FROM L1 AS A CROSS JOIN L1 AS B),
L3 AS(SELECT 1 AS C FROM L2 AS A CROSS JOIN L2 AS B),
L4 AS(SELECT 1 AS C FROM L3 AS A CROSS JOIN L3 AS B),
L5 AS(SELECT 1 AS C FROM L4 AS A CROSS JOIN L4 AS B),
Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS N FROM L5)
SELECT N, CASE WHEN N % #divisor = 0 THEN 'Div' + CAST(#divisor AS VARCHAR(100)) ELSE NULL END AS Col2 FROM Nums
WHERE N <= #limit
The 2 variables determine what number you're looking for the first column to be divisible by, the second for how far you want to go, the next bit is just a CTE to generate the numbers for the first column (numbers tables are really useful for loads of stuff like this). Then it's just selecting all the numbers from the numbers table up to your limit and a case expression to check whether it's divisible by the number you specify (remainder 0) and a bit of string concatenation for the DivX bit.
You should easily be able to integrate this logic into your function.
You are looking for the Modulo operator that basically returns the remainder of a division problem.
DECLARE #SOMETBL TABLE (ROWNUM INT, DIVSTATUS CHAR(4))
INSERT #SOMETBL
(ROWNUM)
SELECT 1
UNION
SELECT 5
UNION
SELECT 2
UNION
SELECT 10
UPDATE #SOMETBL
SET DIVSTATUS = CASE WHEN ROWNUM%5 > 0 THEN NULL ELSE 'DIV5' END
SELECT * FROM #SOMETBL
Related
I want to split a string like ('abc', 'dev', dfg') and insert into a table with its order number like that:
index value
1 abc
2 dev
3 dfg
Is anyway I can do this in mysql? The size is unknown.
You probably can do something like this:
WITH RECURSIVE cte AS (
SELECT 1 idx, LENGTH(REGEXP_REPLACE(stringVal,'[^,]',''))+1 AS maxLen
FROM mytable UNION
SELECT idx+1, maxLen FROM cte WHERE idx+1 <= maxLen)
SELECT idx,
SUBSTRING_INDEX(SUBSTRING_INDEX(stringVal,',',idx),',',-1) AS val
FROM cte
CROSS JOIN mytable;
Generating idx using cte based on how many strings were separated by comma in the column. The steps are:
Use REGEXP_REPLACE() to replace any value that is not comma; returns ,,.
Use LENGTH() on the regexp to get the total of comma; returns 2.
Add 1 (+1) to the length result returned; final value 3.
The cte result are:
+-----+--------+
| idx | maxLen |
+-----+--------+
| 1 | 3 |
| 2 | 3 |
| 3 | 3 |
+-----+--------+
From the generated cte , we're going to use idx value with SUBSTRING_INDEX() to separate the comma-separated, single-row string into multiple rows.
Understanding what the query is doing:
This, we can emulate with 3 of the same query repeated UNION ALL together like the following:
SELECT 1 AS 'index',
SUBSTRING_INDEX(SUBSTRING_INDEX(stringVal,',',1),',',-1) AS val
FROM mytable
UNION ALL
SELECT 2,
SUBSTRING_INDEX(SUBSTRING_INDEX(stringVal,',',2),',',-1)
FROM mytable
UNION ALL
SELECT 3,
SUBSTRING_INDEX(SUBSTRING_INDEX(stringVal,',',3),',',-1)
FROM mytable;
Which also can be done as such; Same idea as the cte approach:
SELECT idx,
SUBSTRING_INDEX(SUBSTRING_INDEX(stringVal,',',idx),',',-1) AS val
FROM (SELECT 1 idx UNION
SELECT 2 UNION
SELECT 3 ) i
CROSS JOIN mytable;
Both of these query return the same result as the cte approach however it's not an ideal one; since we have to manually check how many string separated by comma and define (hard-code) it in the query. Imagine if a single row of string consists of something like a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z - all the 26 alphabet; then that means the UNION query need to be repeated 26 times.
Demo fiddle
Is there any way to create a table with column containing integer from 0 to 1000 without using loops?
Regards,
KC
You can use a recursive CTE:
with recursive n as (
select 1 as n
union all
select n + 1
from n
where n < 1000
)
select n
from n;
CROSS JOIN will be faster than a recursive CTE (kudos to Itzik Ben-Gan)
WITH
L0 AS ( SELECT 1 AS c
FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ),
L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),
L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),
Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
FROM L2 )
SELECT rownum - 1 AS n
FROM Nums
ORDER BY rownum
LIMIT 1000;
Schema (MySQL v8.0)
Query #1
with recursive numbers(id) as (
select 0 as id
union all
select id+1 from numbers where id < 999
)
select id from numbers;
id
0
1
2
3
4
5
6
.
.
.
997
998
999
View on DB Fiddle
I have a table with several columns, and four of them are just generic integer values (num1, num2, num3, num4) and the source of data has these values in random order. In php I would fetch the row, assign the four ints to an array, sort the array, assign the sorted values back and save the row. I would like to do this with sql. So, with a table like
id, num1, num2, num3, num4
1 6 2 9 1
2 12 3 8 4
...
I would like the results to be
id, num1, num2, num3, num4
1 1 2 6 9
2 3 4 8 12
...
I have Googled everything I can think of and scoured the Mysql ref manual but can not seem to find a way to do this. Any thoughts or should I just write a php script?
Well that's an unpivot, a sort and a pivot, but last I heard not in mysql so
maybe
Set #position = 0
CREATE TEMPORARY TABLE OrderedUnpivot AS (
Select Id,#position = #position + 1, #position % 4 as Position, Value From
(
Select Id,Position,Value From
(
select Id, 1 as Position, num1 as Value
Union
select Id, 2, num2
union
select Id, 3, num3
union
select Id, 4, num4
) depivot
Order by Id, Value
)
Select ID,V1.Value as Num1, V2.Value as Num2, V3.Value as Num3, V4.Value as Num4
From OrderedUnpivot v1
Inner join OrderedUnpivot v2 ON v1.Id = v2.ID
Inner join OrderedUnpivot v3 ON v1.Id = v3.ID
Inner join OrderedUnpivot v4 ON v1.Id = v4.ID
Messy but food for thought, oh and not tested or even syntax checked.
I have a doubt on how variables work in mysql. As I read in their web looks like setting a variable will become visible to the next row.
My table is like:
A B C N
1 NULL NULL 4
1 NULL NULL 4
1 1 NULL 4
1 1 NULL 4
1 1 1 4
1 1 1 4
What I want is to return only the rows with C = 1. If no rows then return B = 1 and C is NULL if no rows A = 1 and B is NULL and C is NULL.
My idea was:
select N as number,
#var_c := case when (C = 1) then 1 else -1 end as myc,
#var_b := case when (#var_c < 0 and B = 1) then 1 else -1 end as myB,
#var_c := case when (#var_a < 0 and var_b < 0 and C = 1) then 1 else -1 end as myC
from (select #var_a := -1) r_a,
(select #var_b := -1) r_b,
(select #var_c := -1) r_c,
(select A, B, C, N from my_table order by A desc, B desc, C desc) rows
It should (I want to) return
number myA myB myC
4 -1 -1 1
4 -1 -1 1
4 -1 -1 -1
4 -1 -1 -1
4 -1 -1 -1
4 -1 -1 -1
With this and having myA > 0 or myB > 0 or myC > 0 would work.
But it is returning
number myA myB myC
4 1 -1 -1
4 1 -1 -1
4 -1 -1 1
4 -1 -1 1
4 -1 1 -1
4 -1 1 -1
Shouldn't Mysql keep the vars across the rows?
Regards.
First of all, do not use variables like this. You shouldn't expect that you can set a variable in a FROM clause and then access the value in the SELECT clause. That's not how variables work. Normally you should only use a variable in one place in the query, otherwise you can get undeterministic results.
Second of all, why don't you just issue three different queries? First for A = 1 and B is NULL and C is NULL, and if it doesn't return any rows, issue a query with the second condition set. And so forth.
And if you ultimately want to issue just a single query, you can try this:
SELECT N as number
FROM my_table
WHERE IF(EXISTS (SELECT A FROM my_table WHERE A = 1 and B is NULL and C is NULL),
A = 1 and B is NULL and C is NULL,
IF(EXISTS (SELECT A FROM my_table WHERE B = 1 and C is NULL), B = 1 and C is NULL, C = 1))
But it's very likely to kill performance. So better just use three queries instead of one.
UPD: There's another (yet similar) approach:
SELECT N as number
FROM my_table
WHERE (
A = 1
AND B is NULL
AND C is NULL
) OR (
B = 1
AND C is NULL
AND NOT EXISTS (SELECT A FROM my_table WHERE A = 1 and B is NULL and C is NULL)
) OR (
C = 1
AND NOT EXISTS (SELECT A FROM my_table WHERE A = 1 and B is NULL and C is NULL)
AND NOT EXISTS (SELECT A FROM my_table WHERE B = 1 and C is NULL)
)
From the other sample and it's comments, I would approach your query this way. The first query to pre-qualify if there are ANY "C = 1" values and store into a CCnt field. If none found, force the CCnt value to NULL. SECOND, do the same against the "B" column. Since each of these has a limit of 1, should be fast as long as it can FIND an entry of C=1 or B=1 respectively. if not, their values are NULL. Why do this way? The query is done ONCE per run of the query and the answer is done. It doesn't have to keep doing the query repeatedly as can be KILLER performance when used as sub-queries. Doing a count() with no group by will GUARANTEE as single row, so you won't have any Cartesian result that could result in duplicates.
Now that this preemptive querying is done, now add in your main table. At this point, the "HasCEntry.CCnt" value will either be a 1 or NULL.
"HasBEntry.CCnt" value will either be a 1 or NULL.
select
YT.*
from
( select IF( count(*) > 0, 1, NULL ) as CCnt
from YourTable
where C = 1
limit 1 ) HasCEntry,
( select IF( count(*) > 0, 1, NULL ) as BCnt
from YourTable
where B = 1
limit 1 ) HasBEntry,
YourTable YT
where
YT.C = 1
OR ( YT.B = 1 AND HasCEntry.CCnt IS NULL )
OR ( YT.A = 1 AND HasBEntry.BCnt IS NULL AND HasCEntry.CCnt IS NULL )
Now, following the WHERE clause. If there is even 1 record where C=1, then the
YT.C = 1 is all you need and its done... and the other "OR" conditions you don't care about, the record is coming along.
Now, say there are NO entries for C = 1. We know the "CCnt" value is NULL, so that will come into the first "OR" condition and testing against "B"... B Specifically = 1 AND the CCnt IS NULL... So, if there was an entry for "C=2" (3, 4 or any other value), we don't want it. We only want B = 1 AND C = NULL...
If C and B fail, then we fall into the "A" test. Likewise, A = 1 AND B is null AND C is NULL for same reasons as B above.
For a small table, you might not notice any significant performance difference from Shedal's answer. However, it COULD be a huge hit as your table grows.
I have a table with 100 rows with double as data
a1 a2 a3...
---------
1 2 3
23 55 4
2 3 7
I am planning to use UNION ALL to make that table bigger
a1 a2 a3...
---------
1 2 3
23 55 4
2 3 7
1 2 3
23 55 4
2 3 7
1 2 3
23 55 4
2 3 7
This is for testing purposes so what do you recommend, what would be the most efficient way to do this?
This will increase the size of your table exponentially... First it inserts x records, then 2x, then 4x, then 8x... You could add distinct or top n, etc. to the select if you just want to add the same number of records each time.
DECLARE #count int
DECLARE #max int
SET #count int = 1
SET #max = 10
WHILE #count < #max
BEGIN
INSERT INTO myTable (a1, a2, a3)
SELECT a1, a2, a3 FROM myTable
SET #count = #count + 1
END
BTW -- not sure what you're trying to test, but you might add something besides whole numbers to your data set -- e.g., 1.01, .99, 55.7, 60, etc.
EDIT
Per your comment -- if you really want to use union all then...
INSERT INTO myTABLE (a1, a2, a3)
SELECT a1, a2, a3 FROM
(
SELECT a1, a2, a3 FROM myTable
UNION ALL
SELECT a1, a2, a3 FROM myTable
UNION ALL
SELECT a1, a2, a3 FROM myTable
...
) a
INSERT dbo.Table
SELECT /* TOP (n) */ t1.a1, t1.a2, t1.a3
FROM dbo.Table AS t1
CROSS JOIN dbo.Table AS t2
-- repeat CROSS JOINs as necessary
The first cross join will square, second cross join will cube, etc. Luke #kuru's answer you can limit the number of rows added using TOP in case you don't want to do the math.