Related
I have one string element, for example : "(1111, Tem1), (0000, Tem2)" and hope to generate a data table such as
var1
var2
1111
Tem1
0000
Tem2
This is my code, I created the lag token and filter with odd rows element.
with var_ as (
select '(1111, Tem1), (0000, Tem2)' as pattern_
)
select tbb1.*, tbb2.result_string as result_string_previous
from(
select tb1.*,
min(token) over(partition by 1 order by token asc rows between 1 preceding and 1 preceding) as min_token
from
table (
strtok_split_to_table(1, var_.pattern_, '(), ')
returns (outkey INTEGER, token INTEGER, result_string varchar(20))
) as tb1) tbb1
inner join (select min_token, result_string from tbb1) tbb2
on tbb1.token = tbb2.min_token
where (token mod 2) = 0;
But it seems that i can't generate new variables in "from" step and applied it directly in "join" step.
so I wanna ask is still possible to get the result what i want in my procedure? or is there any suggestion?
Thanks for all your assistance.
I wouldn't split / recombine the groups. Split each group to a row, then split the values within the row, e.g.
with var_ as (
select '(1111, Tem1), (0000, Tem2)' as pattern_
),
split1 as (
select trim(leading '(' from result_string) as string_
from
table ( /* split at & remove right parenthesis */
regexp_split_to_table(1, var_.pattern_, '\)((, )|$)','c')
returns (outkey INTEGER, token_nbr INTEGER, result_string varchar(256))
) as tb1
)
select *
from table(
csvld(split1.string_, ',', '"')
returns (var1 VARCHAR(16), var2 VARCHAR(16))
) as tb2
;
How should i write procedure which will check next free number (which is string) based on input.
Procedure should have 2 input value, first one is user input (numbers) and second one is maximum amount of characters in string.
This is procedure i tried to write:
CREATE DEFINER=`root`#`localhost` PROCEDURE `getfreenum`(IN num CHAR(20), IN maxval CHAR(20))
begin
set #compare := (num + num *10);
set #maxId := (select sifra from artikli where sifra >= #compare order by sifra asc limit 1);
while #compare = #maxId do
set #compare := #compare + 1;
set #maxId = (select sifra from artikli where sifra >= #compare order by sifra asc limit 1);
end while;
select #compare;
end
This procedure finds me next available value after my input but it does not include my input in that number, meaning if i call procedure Call getfreenum(1,5) i get value 779 but i should get next 5 char value which includes input number, which is 1.
So a Call getfreenum(1,5) procedure should return 10043 if everything is taken from 10000 to and including 100042 and Call getfreenum(11,5) should check for remaining 3 characters and return let's say 11000. Or 11600 if everything is taken from 11000 to 11599. And it should work like that for every input, even if i enter 4 characters: Call getfreenum(1234,5) procedure should check for 12340, 12341, 12342 and if 12349 is free it should return that, but it should not return value which changes input number, meaning, if I call Call getfreenum(1234,5) and everything is taken including '123459' then function should return NULL or some fixed value for all errors.
Function is used for asigning item numbers to items in a shop. Sometimes for some items maximum number of digits is 3 and sometimes it is 5. Some items have starting numbers: let's say "1254" for cigarettes. and "12" are starting numbers for luxury goods. It is easier for cashier to use this logic when assigning item numbers. it's just more complex for me :) #Schwern – stacks 3 mins ago
This is better handled with better schema design.
Recognize that "1254" is really two parts. There's the category ID 12 and the item ID 54. Rather than store "1254" you'd store these two separately.
create table item_categories(
id integer primary key auto_increment,
shop_id_padding integer not null default 5,
name text not null
);
create table items (
id integer primary key auto_increment,
name text not null,
category_id integer not null,
foreign key(category_id) references item_categories(id)
);
An explicit item_categories table gives us referential integrity, a place to store the category name, as well as how much padding to use.
Now you can let auto_increment do its job.
insert into item_categories (id, name, shop_id_padding) values (12, "cigarettes", 2);
insert into items (name, category_id) values ("Coffin Nails", 12);
insert into items (name, category_id) values ("Death Sticks", 12);
select * from items;
+----+--------------+-------------+
| id | name | category_id |
+----+--------------+-------------+
| 1 | Coffin Nails | 12 |
| 2 | Death Sticks | 12 |
+----+--------------+-------------+
Construct the shop ID using concat. Pad the ID according to the category.
select concat(i.category_id, lpad(i.id, cat.shop_id_padding, '0')) as shop_id
from items i
join item_categories cat on i.category_id = cat.id;
+---------+
| shop_id |
+---------+
| 1201 |
| 1202 |
+---------+
You can get the set of all sifra where sifra + 1 does not exist with NOT EXISTS and a correlated subquery.
num * power(10, maxval - floor(log10(num)) - 1) gives you the minimum number, e.g. 21000 for 21, 5 and num * power(10, maxval - floor(log10(num)) - 1) + power(10, maxval - floor(log10(num)) - 1) gives you one more than the maximum number, e.g. 22000 for 21, 5. Compare sifra + 1 against it accordingly.
Finally you have to make sure the requested number of digits does not exceed the number of digits for the given prefix. I.e. floor(log10(num)) < maxval has to be satisfied.
To make sure at least the predecessor num * power(10, maxval - floor(log10(num)) - 1) - 1 of the minimum number exists use UNION ALL to add it to the base set.
Like that you can calculate the number with just a SELECT, without any (possibly relatively slow) loops.
CREATE PROCEDURE getfreenum
(IN num integer,
IN maxval integer)
BEGIN
SELECT CASE
WHEN NOT EXISTS (SELECT *
FROM artikli t2
WHERE t2.sifra = num * power(10, maxval - floor(log10(num)) - 1)) THEN
num * power(10, maxval - floor(log10(num)) - 1)
ELSE
min(t1.sifra) + 1
END sifra
FROM artikli t1
WHERE floor(log10(num)) < maxval
AND EXISTS (SELECT *
FROM artikli t2
WHERE sifra = num * power(10, maxval - floor(log10(num)) - 1))
AND NOT EXISTS (SELECT *
FROM artikli t2
WHERE t2.sifra = t1.sifra + 1)
AND t1.sifra >= num * power(10, maxval - floor(log10(num)) - 1) - 1
AND t1.sifra < num * power(10, maxval - floor(log10(num)) - 1) + power(10, maxval - floor(log10(num)) - 1) - 1;
END;
DB Fiddle
And if you're dealing with numbers you should use an appropriate type, not char.
There is a SQL table mytable that has a column mycolumn.
That column has text inside each cell. Each cell may contain "this.text/31/" or "this.text/72/" substrings (numbers in that substrings can be any) as a part of string.
What SQL query should be executed to display a list of unique such substrings?
P.S. Of course, some cells may contain several such substrings.
And here are the answers for questions from the comments:
The query supposed to work on SQL Server.
The prefered output should contain the whole substring, not the numeric part only. It actually could be not just the number between first "/" and the second "/".
And it is varchar type (probably)
Example:
mycolumn contains such values:
abcd/eftthis.text/31/sadflh adslkjh
abcd/eftthis.text/44/khjgb ljgnkhj this.text/447/lhkjgnkjh
ljgkhjgadsvlkgnl
uygouyg/this.text/31/luinluinlugnthis.text/31/ouygnouyg
khjgbkjyghbk
The query should display:
this.text/31/
this.text/44/
this.text/447/
How about using a recursive CTE:
CREATE TABLE #myTable
(
myColumn VARCHAR(100)
)
INSERT INTO #myTable
VALUES
('abcd/eftthis.text/31/sadflh adslkjh'),
('abcd/eftthis.text/44/khjgb ljgnkhj this.text/447/lhkjgnkjh'),
('ljgkhjgadsvlkgnl'),
('uygouyg/this.text/31/luinluinlugnthis.text/31/ouygnouyg'),
('khjgbkjyghbk')
;WITH CTE
AS
(
SELECT MyColumn,
CHARINDEX('this.text/', myColumn, 0) AS startPos,
CHARINDEX('/', myColumn, CHARINDEX('this.text/', myColumn, 1) + 10) AS endPos
FROM #myTable
WHERE myColumn LIKE '%this.text/%'
UNION ALL
SELECT T1.MyColumn,
CHARINDEX('this.text/', T1.myColumn, C.endPos) AS startPos,
CHARINDEX('/', T1.myColumn, CHARINDEX('this.text/', T1.myColumn, c.endPos) + 10) AS endPos
FROM #myTable T1
INNER JOIN CTE C
ON C.myColumn = T1.myColumn
WHERE SUBSTRING(T1.MyColumn, C.EndPos, 100) LIKE '%this.text/%'
)
SELECT DISTINCT SUBSTRING(myColumn, startPos, EndPos - startPos)
FROM CTE
Having a table named test with the following data:
COLUMN1
aathis.text/31/
this.text/1/
bbbthis.text/72/sksk
could this be what you are looking for?
select SUBSTR(COLUMN1,INSTR(COLUMN1,'this.text', 1 ),INSTR(COLUMN1,'/',INSTR(COLUMN1,'this.text', 1 )+10) - INSTR(COLUMN1,'this.text', 1 )+1) from test;
result:
this.text/31/
this.text/1/
this.text/72/
i see your problem:
Assume the same table as above but now with the following data:
this.text/77/
xxthis.text/33/xx
xthis.text/11/xxthis.text/22/x
xthis.text/1/x
The following might help you:
SELECT SUBSTR(COLUMN1, INSTR(COLUMN1,'this.text', 1 ,1), INSTR(COLUMN1,'/',INSTR(COLUMN1,'this.text', 1 ,1)+10) - INSTR(COLUMN1,'this.text', 1 ,1)+1) FROM TEST
UNION
SELECT CASE WHEN (INSTR(COLUMN1,'this.text', 1,2 ) >0) THEN
SUBSTR(COLUMN1, INSTR(COLUMN1,'this.text', 1,2 ), INSTR(COLUMN1,'/',INSTR(COLUMN1,'this.text', 1 ,2),2) - INSTR(COLUMN1,'this.text', 1,2 )+1) end FROM TEST;
it will generate the following result:
this.text/1/
this.text/11/
this.text/22/
this.text/33/
this.text/77/
The downside is that you need to add a select statement for every occurance you might have of "this.text". If you might have 100 "this.text" in the same cell it might be a problem.
SQL> select SUBSTR(column_name,1,9) from tablename;
column_name
this.text
SELECT REGEXP_SUBSTR(column_name,'this.text/[[:digit:]]+/')
FROM table_name
I have a column in one of my tables, which is TIME format (00:00:00). I am trying to sum the entire column and display it as same (00:00:00).
I have tried using the following but it is not giving me anywhere near the correct answer.It's giving me 22.12:44:00 and manual calcaulation tells me it should be close to 212:something:something
SELECT SEC_TO_TIME( SUM( TIME_TO_SEC( vluchttijd ) ) ) AS totaltime FROM tbl_vluchtgegevens
Any recommendations?
You can try like this:-
SELECT SEC_TO_TIME(SUM(SECOND(vluchttijd ))) AS totaltime FROM tbl_vluchtgegevens;
or try this(althoug this is not a good approach):
SELECT concat(floor(SUM( TIME_TO_SEC( `vluchttijd ` ))/3600),":",floor(SUM( TIME_TO_SEC( `vluchttijd ` ))/60)%60,":",SUM( TIME_TO_SEC( `vluchttijd ` ))%60) AS total_time
FROM tbl_vluchtgegevens;
Edit:-
Try this:-
select cast(sum(datediff(second,0,dt))/3600 as varchar(12)) + ':' +
right('0' + cast(sum(datediff(second,0,dt))/60%60 as varchar(2)),2) +
':' + right('0' + cast(sum(datediff(second,0,dt))%60 as varchar(2)),2)
from TestTable
Working SQL Fidlle
In MySQL, the TIME type is rather limited in range. Moreover many time function do not accept values greater that 23:59:59, making it really usable only to represent the time of the day.
Given your needs, your best bet is probably to write a custom function that will mimic SEC_TO_TIME but allowing much greater range:
CREATE FUNCTION SEC_TO_BIGTIME(sec INT)
RETURNS CHAR(10) DETERMINISTIC
BEGIN
SET #h = sec DIV 3600;
SET #m = sec DIV 60 MOD 60;
SET #s = sec MOD 60;
RETURN CONCAT(
LPAD(#h, 4, '0'),
':',
LPAD(#m, 2, '0'),
':',
LPAD(#s, 2, '0')
);
END;
And here is how to use it:
create table tbl (dt time);
insert tbl values
('09:00:00'), ('01:00:00'), ('07:50:15'), ('12:00:00'),
('08:30:00'), ('00:45:00'), ('12:10:30');
select SEC_TO_BIGTIME(sum(time_to_sec(dt))) from tbl;
Producing:
+--------------------------------------+
| SEC_TO_BIGTIME(SUM(TIME_TO_SEC(DT))) |
+--------------------------------------+
| 0051:15:45 |
+--------------------------------------+
See http://sqlfiddle.com/#!8/aaab8/1
Please note the result is a CHAR(10) in order to overcome TIMEtype limitations. Depending how you plan to use that result, that means that you may have to convert from that string to the appropriate type in your host language.
This worked for me:
SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(vluchttijd))) AS totaltime FROM tbl_vluchtgegevens;
Is there a way to detect if a value is a number in a MySQL query? Such as
SELECT *
FROM myTable
WHERE isANumber(col1) = true
You can use Regular Expression too... it would be like:
SELECT * FROM myTable WHERE col1 REGEXP '^[0-9]+$';
Reference:
http://dev.mysql.com/doc/refman/5.1/en/regexp.html
This should work in most cases.
SELECT * FROM myTable WHERE concat('',col1 * 1) = col1
It doesn't work for non-standard numbers like
1e4
1.2e5
123. (trailing decimal)
If your data is 'test', 'test0', 'test1111', '111test', '111'
To select all records where the data is a simple int:
SELECT *
FROM myTable
WHERE col1 REGEXP '^[0-9]+$';
Result: '111'
(In regex, ^ means begin, and $ means end)
To select all records where an integer or decimal number exists:
SELECT *
FROM myTable
WHERE col1 REGEXP '^[0-9]+\\.?[0-9]*$'; - for 123.12
Result: '111' (same as last example)
Finally, to select all records where number exists, use this:
SELECT *
FROM myTable
WHERE col1 REGEXP '[0-9]+';
Result: 'test0' and 'test1111' and '111test' and '111'
SELECT * FROM myTable
WHERE col1 REGEXP '^[+-]?[0-9]*([0-9]\\.|[0-9]|\\.[0-9])[0-9]*(e[+-]?[0-9]+)?$'
Will also match signed decimals (like -1.2, +0.2, 6., 2e9, 1.2e-10).
Test:
drop table if exists myTable;
create table myTable (col1 varchar(50));
insert into myTable (col1)
values ('00.00'),('+1'),('.123'),('-.23e4'),('12.e-5'),('3.5e+6'),('a'),('e6'),('+e0');
select
col1,
col1 + 0 as casted,
col1 REGEXP '^[+-]?[0-9]*([0-9]\\.|[0-9]|\\.[0-9])[0-9]*(e[+-]?[0-9]+)?$' as isNumeric
from myTable;
Result:
col1 | casted | isNumeric
-------|---------|----------
00.00 | 0 | 1
+1 | 1 | 1
.123 | 0.123 | 1
-.23e4 | -2300 | 1
12.e-5 | 0.00012 | 1
3.5e+6 | 3500000 | 1
a | 0 | 0
e6 | 0 | 0
+e0 | 0 | 0
Demo
Returns numeric rows
I found the solution with following query and works for me:
SELECT * FROM myTable WHERE col1 > 0;
This query return rows having only greater than zero number column that col1
Returns non numeric rows
if you want to check column not numeric try this one with the trick (!col1 > 0):
SELECT * FROM myTable WHERE !col1 > 0;
This answer is similar to Dmitry, but it will allow for decimals as well as positive and negative numbers.
select * from table where col1 REGEXP '^[[:digit:]]+$'
use a UDF (user defined function).
CREATE FUNCTION isnumber(inputValue VARCHAR(50))
RETURNS INT
BEGIN
IF (inputValue REGEXP ('^[0-9]+$'))
THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END;
Then when you query
select isnumber('383XXXX')
--returns 0
select isnumber('38333434')
--returns 1
select isnumber(mycol) mycol1, col2, colx from tablex;
-- will return 1s and 0s for column mycol1
--you can enhance the function to take decimals, scientific notation , etc...
The advantage of using a UDF is that you can use it on the left or right side of your "where clause" comparison. this greatly simplifies your SQL before being sent to the database:
SELECT * from tablex where isnumber(columnX) = isnumber('UnkownUserInput');
hope this helps.
Another alternative that seems faster than REGEXP on my computer is
SELECT * FROM myTable WHERE col1*0 != col1;
This will select all rows where col1 starts with a numeric value.
Still missing this simple version:
SELECT * FROM myTable WHERE `col1` + 0 = `col1`
(addition should be faster as multiplication)
Or slowest version for further playing:
SELECT *,
CASE WHEN `col1` + 0 = `col1` THEN 1 ELSE 0 END AS `IS_NUMERIC`
FROM `myTable`
HAVING `IS_NUMERIC` = 1
You can use regular expression for the mor detail https://dev.mysql.com/doc/refman/8.0/en/regexp.html
I used this ^([,|.]?[0-9])+$. This is allows handle to the decimal and float number
SELECT
*
FROM
mytable
WHERE
myTextField REGEXP "^([,|.]?[0-9])+$"
I recommend: if your search is simple , you can use `
column*1 = column
` operator interesting :) is work and faster than on fields varchar/char
SELECT * FROM myTable WHERE column*1 = column;
ABC*1 => 0 (NOT EQU **ABC**)
AB15*A => 15 (NOT EQU **AB15**)
15AB => 15 (NOT EQU **15AB**)
15 => 15 (EQUALS TRUE **15**)
SELECT * FROM myTable WHERE sign (col1)!=0
ofcourse sign(0) is zero, but then you could restrict you query to...
SELECT * FROM myTable WHERE sign (col1)!=0 or col1=0
UPDATE: This is not 100% reliable, because "1abc" would return sign of
1, but "ab1c" would return zero... so this could only work for text that does not begins with numbers.
you can do using CAST
SELECT * from tbl where col1 = concat(cast(col1 as decimal), "")
I have found that this works quite well
if(col1/col1= 1,'number',col1) AS myInfo
Try Dividing /1
select if(value/1>0 or value=0,'its a number', 'its not a number') from table