Data range using Between in SQL Server - sql-server-2008

I am trying to select data in different ranges for a category using IN but not quite sure the syntax
ex: a category has the number 1,3,4 then 20 to 30, then 100 to 110
I use the syntax
Case category =
When categoryId IN (1,3,4, [20-30],[100-110] Then 'Running'
But I received syntax error. How do I do it?

You could use
SELECT
CASE
WHEN EXISTS (SELECT *
FROM (VALUES(1,1),
(3,4),
(20,30),
(100,110)) Ranges(Low, High)
WHERE categoryId BETWEEN Low AND High) THEN 'Running'
END
FROM YourTable

CASE
WHEN categoryId IN (1,3,4) OR
(categoryId >= 20 AND categoryId <= 30) OR
(categoryId >= 100 AND categoryId <= 110)
THEN 'Running'
END

You can write a simple scalar function as i normally do .
CREATE FUNCTION [dbo].[Categoory_Range] (#CategoryID as bigInt)
RETURNS bit AS
BEGIN
Declare #result as bit = 0
-- Need to tack a delimiter onto the end of the input string if one doesn't exist
if(#CategoryID <= 4 and #CategoryID > 0)
begin
set #result = 1
end
else if(#CategoryID >20 and #CategoryID <= 30)
begin
set #result =1
end
else if(#CategoryID > 100 and #CategoryID <= 110)
begin
set #result = 1
end
else
begin
set #result= 0
end
return #result
END
When and where ever you want to use like
Select
Case WHEN [Categoory_Range](CategoryID) > 0 Then 'Running' ELSE 'Whatever' END FROM MyTable.
I love using the functions for you don't have to rewrite this all the time. I normally do it with functions. Check the function above didn't test though. Hope it helps. However if you just want to write it once, you can just want to write once, you can do in the query.

you can try this:
CASE
WHEN CategoryId in (1,3,4) or
(CategoryId between 20 and 30) or
(CategoryId between 100 and 110) THEN 'Running'
END

Related

My MySQL function doesn't quite do what I want

This came from a question in Python, I thought, I could do this directly in MySQL. The guy said, the .csv file has millions of rows, so I think this came originally from a MySQL table.
I have never made a MySQL function before, so it's all a bit new.
A simple table, 3 columns: id, price and signal_
The task is, look at each price.
The first reference price is the price in row 1 and going down.
Going down, if the price has increased by 1 or more, write 1 in the column signal_
Now the price in that row is the reference price.
Or if the price has decreased by 1 or more, that price is the new reference price.
Always write 1 in column signal_ if the price has increased by 1 or more.
Always write -1 in column signal_ if the price has decreased by 1 or more.
The column signal contains 0 to begin with.
I made this function, but it doesn't get everything right. I think, maybe the scope of the variables is causing the problem. Maybe #start is a local variable in the function and another variable in MySQL
DELIMITER //
CREATE FUNCTION comparePrice(p DECIMAL)
RETURNS INT DETERMINISTIC
BEGIN
DECLARE signalnum INT;
# #start is the first value in row 1 when we start
# if p > #start
IF p - #start >= 1 THEN SET signalnum := 1, #start := p;
# if #start - p >= 1 p is smaller than #start by 1 or more
ELSEIF #start - p >= 1 THEN SET signalnum := -1, #start := p;
# if the change is less than 1, signalnum = 0
ELSE SET signalnum := 0, #start = p;
END IF;
RETURN signalnum;
END; //
DELIMITER ;
I looked at threads for getting the value of the next price but I couldn't make it work.
# initialize #start
SELECT #start := price FROM prices_up_down WHERE id = 1;
UPDATE prices_up_down SET signal_ = comparePrice(price);
Maybe it will never work like this!
I will be very grateful for any advice!
EDIT: the problem is my user variable #start gets truncated, resulting in wrong values in the column signal_
Anyone know why #start is truncated??
You can use a common table expression (cte) to get each row along with the previous value, then just update signal with an expression comparing the two;
WITH cte AS (
-- Get id and price along with the price from the previous row (by id)
SELECT id, price new_price, LAG(price) OVER (ORDER BY id) old_price
FROM prices_up_down
)
-- Join the cte with the original table since cte's aren't updatable in MySQL
UPDATE prices_up_down JOIN cte ON prices_up_down.id = cte.id
-- Set signal to 1 if the diff in price is > 1, or -1 if it's < -1
SET `signal` =
CASE WHEN new_price-old_price > 1 THEN 1
WHEN new_price-old_price < -1 THEN -1
ELSE 0
END;
A DBfiddle to test with
As always, if the data is important, always back up your table and test things before running updates from random people on the Internet.

Preventing duplicates while selecting into variable in MySQL

My function generates unique combinations from Items table and stores into list variable. In the end of generation, it returns list as a result and application processes every combination: saves into Combo table.
The Problem
It checks every time for duplicates from another tables called Combo which is getting filled in the second step of process (by application, not by function itself.)
But, It doesn't check for duplicates inside listvariable before inserting into it newly generated combinations.
So I'm getting result from function with duplicates inside the result itself. For example. 3423 appears here in the result 2 times:
3410;3463;3423;3489;3446;3445;3417;3436;3497;3454;3491;3420;3502;3496;3458;3493;3439;3499;3497;3487;3486;3504;3458;3501;3503;3441;3443;3453;3508;3474;3469;3497;3508;3433;3451;3449;3422;3453;3428;3475;3474;3458;3480;3422;3488;3432;3501;3414;3425;3444;3509;3502;3440;3422;3472;3501;3477;3483;3449;3480;3456;3463;3493;3476;3479;3425;3485;3464;3410;3434;3488;3504;3439;3423;3434;3486;3448;3456;3496;3413;3428;3482;3439;3437;3473;3420;3439;3470;3463;3494;3415;3442;3428;3500;3488;3478;3475;3417;3472;3463
How can I check list itself for duplicates before insertion?
Details
My function:
SELECT gen_n_uniq_perms_by_cat(1, 100, 1, 45, 1, 120, 20) as comb
which look like:
BEGIN
SET #result := "";
SET #counter := 0;
iterat :
LOOP
SELECT
gen_uniq_perm_by_cat(
permSize ,
user_id ,
catID ,
itemType ,
tsc_id ,
tries
) INTO #combo;
IF(ISNULL(#combo)) THEN
RETURN #result;
ELSE
SET #result := CONCAT_WS(';' ,#result ,#combo);
END
IF;
SET #counter := #counter + 1;
IF #counter > permCount THEN
RETURN #result;
END
IF;
END
LOOP
iterat;
END
and gen_uniq_perm_by_cat looks like:
BEGIN
iterat :
LOOP
SELECT
SUBSTRING_INDEX(
GROUP_CONCAT(`id` ORDER BY RAND() SEPARATOR '-') ,
'-' ,
permSize
) INTO #list
FROM
`Item`
LEFT JOIN `ItemCategory` ON `Item`.`id` = `ItemCategory`.`itemID`
WHERE
(`Item`.`user_id` = user_id)
AND(`ItemCategory`.`catID` = catID)
AND(`Item`.`type` = itemType);
SET #md5 := MD5(CONCAT_WS('-' , #list , tsc_id));
IF(
SELECT
count(*)
FROM
`Combo`
WHERE
`Combo`.`hash` = #md5
LIMIT 1
) = 0 THEN
RETURN #list;
END
IF;
SET tries := tries - 1;
IF tries = 0 THEN
RETURN NULL;
END
IF;
END
LOOP
iterat;
END
generates unique (never created in past) combinations by following arguments:
permSize = 1
permCount =100
user_id = 1
catID = 45
itemType = 1
tsc_id = 120
tries = 20
Use NOT LIKE for this purpose. In your case, replace corresponding condition lines with following:
IF(ISNULL(#combo)) THEN
RETURN #result;
END IF;
IF(#result NOT LIKE CONCAT('%' , #combo , '%')) THEN
SET #result := CONCAT_WS(';' ,#result ,#combo);
SET #counter := #counter + 1;
END IF;
IF #counter = permCount THEN
RETURN #result;
END IF;

stored procedure returns wrong value

I have a stored procedure that keeps giving me wrong answer. I asked the procedure to return the value of motor insurance. I run the procedure and give me the total of motor insurance premium but if I run it for the 4th time it give me the ageRange select statement value.
I moved the code into a new procedure but still the same.
My code
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `cal_motor_qoute`(in
coverID int , in dob date,
in sumMotor double , out QMsg varchar(200))
BEGIN
declare policy_cover , total , insRatio, ageExtra double;
declare ageRange int;
declare price_list varchar(200);
SELECT DATEDIFF(NOW(),dob) / 365.25 AS ageRange from dual;
if (coverID = 1) then
set policy_cover = 0.002;
elseif (coverID = 2) then
set policy_cover = 0.0025;
elseif (coverID = 3) then
set policy_cover = 0.003;
elseif (coverID = 4) then
set policy_cover = 0.0035;
end if;
if ( ageRange < 25) then
set ageExtra = 0.0005;
else
set ageExtra = 0.000;
end if;
set insRatio = policy_cover + ageExtra;
set total = (sumMotor * insRatio )* 10;
set QMsg = concat('total Premium is: ',total);
select #QMsg;
END
Any help please..
SELECT DATEDIFF(NOW(),dob) / 365.25 AS ageRange from dual;
will not set the variable ageRange, but it will do a select (of the calculated value) and name the column of the resultset ageRange.
The (or rather: one) way to set the value of your variable is to use into:
SELECT DATEDIFF(NOW(),dob) / 365.25 into ageRange from dual;
Although this is probably not the most precise way to calculate the age of a person anyway. You might want to replace
if ( ageRange < 25) then
with
if ( dob > date_sub(now(), interval 25 year) ) then

How to get the number of business days using mysql

I have to find the number of business days using mysql.I am using this query but this is not giving me the correct result
SELECT ((DATEDIFF('2015-05-31', '2015-05-01')) -((WEEK('2015-05-31') - WEEK('2015-05-01')) * 2) -
(case when weekday('2015-05-31') = 6 then 1 else 0 end) - (case when weekday('2015-05-01') = 5 then 1 else 0 end))
as DifD ;
It is giving 19 as output where number of business days should be 20
Somebody please help
Try this!
SET #i=-1;
SELECT SUM(CASE WHEN(WEEKDAY(ADDDATE('2015-05-01', INTERVAL #i:=#i+1 DAY))) < 5 THEN 1 ELSE 0 END) AS `business_days`
FROM `table`
WHERE #i < DATEDIFF('2015-05-31', '2015-05-01');
Hope this answer helps!
drop procedure COUNTWEEKDAYS;
DELIMITER $$
CREATE PROCEDURE COUNTWEEKDAYS (FROMDATE TIMESTAMP, TODATE TIMESTAMP)
begin
declare NOOFWEEKDAYS INTEGER;
set NoOfWeekDays = (datediff(todate, fromdate) + 1)
-((timestampdiff(week, FROMDATE , TODATE) * 2))
-weekday(fromdate)%4
-weekday(todate)%4;
select NOOFWEEKDAYS;
end$$

Round to nearest 5 in SQL Server

I have a Money column in my SQL Server 2008 table. In my below query how can I round it to nearest 5$
select FineAmount from tickets
Thanks
select round(FineAmount*2,-1)/2 from tickets
or to put nicholaides suggestion in sql
select round(FineAmount/5,0)*5 from tickets
The example assumes that FineAmount is of type money.
The second approach is probably better as the first one works with the limit of maximum_value_of_money_type/2
More on ROUND
A general math solution:
Divide by 5, round to the nearest integer, then multiply by 5.
If you want to truncate (round-down) to a grouping of 5 use the modulo function; in Microsoft SQL Server this is %
ie: field1 - (field1 % 5)
If you had field1 == 3, then the calc would be:
3 - (3 % 5) = 0
if it were 13:
13 - (13 % 5) = 10
Simply add 5 if you want to round up
See also 'MOD' is not a recognized built-in function name
My first solution was
create function dbo.udf_RoundToNearest(#x int, #to int)
returns int
with schemabinding as begin
return #to * convert(int, round(convert(float, #x) / convert(float, #to), 0))
end
This works, but is considered by MSSQL as 'imprecise' since it uses floating point numbers internally. That stops it being used in indexed views. You can instead do the job only with integer arithmetic:
create function dbo.udf_RoundToNearest(#x int, #to int)
returns int
with schemabinding as begin
declare #m int
set #m = abs(#x) % abs(#to)
declare #trunc int
set #trunc = abs(#x) - #m
declare #r int
set #r = case when #m * 2 >= abs(#to) then #trunc + abs(#to) else #trunc end
return case when #x < 0 then -#r else #r end
end
Round to next greater 5
(CAST(#Amount/5 AS INT) + IIF(CAST(ROUND(#Amount,0) AS INT) % 5>1,1,0))*5)
DECLARE #Amount DECIMAL(18,3) ; SET #Amount = 7818.32
SELECT(Round((#Amount-CAST(#Amount AS INT))*100 /5,0)*5 /100)
+ CAST( #Amount AS INT)
-- you'll get 7818.30
Use ROUND function
SELECT ROUND(FineAmount,5)
FROM tickets