(needs to be compliant code down to Access 2003)
Apologies if this is a "doh!" moment (still learning)...as part of a SELECT statement I have the following field called Total whose purpose, for each record, is to calculate the total of 4 previous fields. A '0' value is passed to Total if the sum is <1000, if it isn't then the actual sum is passed.
Summarised we have:
,IIf(
Switch(……) +
Switch(……) +
Switch(……) +
Switch(……)<1000,0,
Switch(……) +
Switch(……) +
Switch(……) +
Switch(……))
AS Total
In its much expanded version it does work, however, this looks cumbersome since it means having to repeat the initial 4 Switch conditions twice (once if <1000 and once if not).
Is there any way to reduce this?
One method is to use a subquery:
select iif(val < 1000, 0, val)
from (select x.*, (switch() + . . . + switch()) as val
from x
) as x1;
Related
I have the following code in a stored procedure and am trying to conditionally format a calculated number based on its length (if the number is less than 4 digits, pad with leading zeros). However, my case statement is not working. The "formattedNumber2" result is the one I'm looking for.
I'm assuming the case statement treats the variable strangely, but I also don't know of a way around this.
DECLARE #Number int = 5
SELECT
CASE
WHEN (LEN(CONVERT(VARCHAR, #Number)) > 4)
THEN #Number
ELSE RIGHT('0000' + CAST(#Number AS VARCHAR(4)), 4)
END AS formattedNumber,
LEN(CONVERT(VARCHAR, #Number)) AS numberLength,
RIGHT('0000' + CAST(#Number AS VARCHAR(4)), 4) AS formattedNumber2
I get the following results when I run the query:
formattedNumber numberLength formattedNumber2
-------------------------------------------------
5 1 0005
SQL DEMO
The problem is you are using different data type on your case , integer and string. So the CASE stay with the first type he find and convert the rest.
CASE WHEN (LEN(convert(VARCHAR, #Number)) > 4) THEN convert(VARCHAR, #Number)
This can be done a lot easier with format() since version 2012.
format(n,
'0000')
And that would also handle negative values, which your current approach apparently doesn't.
Prior 2012 it can be handled with basically replicate() and + (string concatenation).
isnull(replicate('-',
-sign(n)), '')
+
isnull(replicate('0',
4
-
len(cast(abs(n) AS varchar(10)))
),
'')
+
cast(abs(n) AS varchar(10))
(It targets integer values, choose a larger length for the varchar casts for bigint.)
db<>fiddle
For example I had a column named 'ID'
I want to get the output as
ID
---
ABCXX708
ABCXX976
ABCXX654
ABCXX081
In short ABCXX should be common for every row but the remaining 3 numbers should be random and integer..
with t (n) as (select 0 union all select n+1 from t where n <100)
select 'ABC'
+ format(n,'00')
+ cast(cast(rand(cast(newid() as varbinary(100)))*10 as int) as char(1))
from t
Alternative solution
with t (n) as (select 0 union all select n+1 from t where n <100)
select 'ABC'
+ right ('0' + cast(n as varchar(2)),2)
+ cast(cast(rand(cast(newid() as varbinary(100)))*10 as int) as char(1))
from t
You can write like this
select 'ABCXX'+CAST(FLOOR(RAND()*(1000-100)+100) as varchar(3)) 'id'
With the RAND() function you can get Random numbers. And For the 'ABCXX' you can follow your previous logic.
SELECT CAST(RAND()*10.00 AS INT)
The above RAND() function will give values between 0.0 to 1.0 in decimals every time you hit the Statement. To make it for a single digit Multiply with 10 and Cast it to INT to remove the next decimal values.
Reference " MSDN
Since SQL Server 2012 you have FORMAT function and SEQUENCE object. Hence the below query will work.
First you need to create a Sequence object.
CREATE SEQUENCE DemopSeq
START WITH 1
INCREMENT BY 1;
Then the following query will generate results as per your requirement.
SELECT CONCAT('ABC',FORMAT(NEXT VALUE FOR DemopSeq, '00'),ABS(Checksum(NewID()) % 10))
Hope this helps.
what would the below sql return and why?
SELECT 1 + '+' + 2
Same reason SELECT 1 + 'whocares' + 2 results in 3. In some database systems, 'strings' evaluate to 0 if they can't be interpreted as a number. Try SELECT 1 + '2' + 3 to see an example of where it CAN be interpreted as a number.
And yes, the "in some database systems" applies to this whole answer, where I'm going to guess you're using something like MySQL.
One way of thinking about this type of mathematical logic is that SQL manages your math functions in a given order of operation.
SELECT 1 + 'whocares' + 2
-- Results in a number as the first item processed, 1, tells SQL it is about to deal with integers
SELECT 'whocares' + 1 + '...this guy'
-- Results in a string of 'whocares...this guy' as the first item is a string
Actually, the second line may fail depending on which SQL syntax/system you're using... but you get the point.
In this SQL statement:
select (ISNULL(Tbl1.Bromod,0) + ISNULL(Tbl1.Bromoform,0) +
ISNULL(Tbl1.Chlor,0) + ISNULL(Tbl1.Dibromoc,0)) / 4 )
from TblTruck
I'd like to dynamically change the value that is 4 currently to be the number of values that are NOT NULL.
What I am trying to do is to get the average ignoring NULL entries.
Say if Bromod is NULL and the others are not BULL, I would then divide by 3, as 3 actually have values.
select (
ISNULL(Tbl1.Bromod,0) + ISNULL(Tbl1.Bromoform,0) + ISNULL(Tbl1.Chlor,0) + ISNULL(Tbl1.Dibromoc,0)
)
/ ISNULL(
NULLIF(
CONVERT(INT,CONVERT(BIT,ISNULL(Tbl1.Bromod,0))) + CONVERT(INT,CONVERT(BIT,ISNULL(Tbl1.Bromoform,0))) + CONVERT(INT,CONVERT(BIT,ISNULL(Tbl1.Chlor,0))) + CONVERT(INT,CONVERT(BIT,ISNULL(Tbl1.Dibromoc,0)))
,0
)
,1
)
from TblTruck Tbl1
So,neat trick, all #s other than zero convert as a true when cast as a bit. So, NULLs and 0's become zero and all other values become 1. Sum together and that gives you what you are looking for. This solution also handles a divide by zero issue that would arise if all values were null/zero.
I have some sql that will not return the rows I need unless I specify that as a criteria in the where clause. If I uncomment the part below that is commented out, it will give me the rows I want. If I leave it commented out, those rows are not returned in my result set.
Does this make sense? Can anyone see anything I'm doing wrong? Thanks.
SELECT
RTRIM(c.comp2) + '-' + l.Loc_Name,
MAX(RTRIM(g.mega_location_num) + '-' + g.mega_location_name)
FROM
mkt_share_comp c, gldm_location g, mkt_share_locs l
WHERE
RTRIM(c.comp1) = g.location_num
AND c.comp2 = l.Loc_No
AND LEN(c.comp2) = 5 AND c.is_deleted = 0 AND l.is_deleted = 0
--and g.mega_location_num = '450'
GROUP BY
RTRIM(c.comp2) + '-' + l.Loc_Name
ORDER BY
MAX(RTRIM(g.mega_location_num) + '-' + g.mega_location_name)
This comparison:
MAX(RTRIM(g.mega_location_num) + '-' + g.mega_location_name)
Will be doing a MAX based on the string value that you're constructing. So if there are any g.mega_location_num values which start with a digit greater than 4 (or start with 4 and have a second digit greater than 5, etc), then that value will be the MAX value returned.
To start to fix this, I would first switch to the ANSI join style that Kuya has suggested. I would then consider including an appropriate ROW_NUMBER() expression to locate the "genuine" maximum value from the g table and to be able to retrieve multiple columns from that maximal row (to allow the string construction to proceed)