The log function requires 1 argument(s) at SQL server - sql

I am trying to run this script on SQL Server:
CREATE FUNCTION [dbo].[IPAddressToCidr](#IP AS VARCHAR(15))
RETURNS INT
AS
BEGIN
DECLARE #result INT;
DECLARE #ipInt BIGINT;
SET #ipInt = CONVERT(BIGINT, PARSENAME(#IP, 1)) + CONVERT(BIGINT, PARSENAME(#IP, 2)) * 256 + CONVERT(BIGINT, PARSENAME(#IP, 3)) * 65536 + CONVERT(BIGINT, PARSENAME(#IP, 4)) * 16777216;
SET #result = CAST(( 32 - LOG(4294967296 - #ipInt, 2)) AS INT);
RETURN #result;
END;
The result error is:
Msg 174, Level 15, State 1, Procedure IPAddressToCidr, Line 11
The log function requires 1 argument(s).
I am trying...
SELECT LOG(4294967296,2) /*this has problem*/
SELECT LOG(4294967296) /*this one works but with different results */
Any help with the Log() function with two parameters?

SQL Server 2012+ supports LOG(expr, [base])
With lower version you can use simple Math to get the result:
SELECT LOG(1024)/LOG(2) -- 10, 2 ^ 10 = 1024
SELECT LOG(1000)/LOG(10) -- 3, 10 ^ 3 = 1000
LiveDemo
Change of base formula:
LOG(#arg,base) = LOG(#arg) / LOG(base)

Related

NPER excel function in SQL

Has anybody figured out how to use NPER excel function to SQL? Use case is that I am trying to find the remaining terms of an acct in SQL.
Fields:
Current_principalbalance - PV
Current_interestrate/100/12 - RATE
Current_paymentamount- PMT
No types in data
no FV in data
I tried:
Use NLS
go
declare #fv float
declare #rate float
declare #Pmt float
declare #k float
declare #pv float
set #fv=0
set #rate=(select (current_interest_rate/100/12) from loanacct)
set #pmt= (select amortized_payment_amount from loanacct_payment)
set #pv = (select current_principal_balance from loanacct)
set #k=1
select Log10((-#Fv * (#Rate / #k) + #Pmt)
/ (#Pmt + (#Rate / #k) * #Pv))
/ Log10(1 + #Rate) as nper
from loanacct a, loanacct_detail b, loanacct_setup c, loan_class d, loan_group e, loanacct_payment f
where a.acctrefno = b.acctrefno
and b.acctrefno = c.acctrefno
and a.loan_class2_no = d.codenum
and e.loan_group_no = a.loan_group_no
and f.acctrefno = a.acctrefno
and e.loan_group_no = 55
and a.loan_number IN (66515,67214,65980)
but now i get the error: Msg 512, Level 16, State 1, Line 9
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Since NPER is a forumla it would be possible to implement it in SQL since the operators for a formula would almost always be supported in databases
When using "Log" in your query, the natural logarithm is considered, and nper uses the Log to base 10. Therefore the formula should be altered as
select Log10((-#Fv * (#Rate / #k) + #Pmt)
/ (#Pmt + (#Rate / #k) * #Pv))
/ Log10(1 + #Rate) as nper
Here is a test case which compares the value from excel nper function and one from the sql server database, that matches using your example..
--I have edited the datatypes to be float
declare #fv float
declare #rate float
declare #Pmt float
declare #k float
declare #pv float
set #fv=0
set #rate=15.99/100/12
set #pmt=-167.65
set #pv =1491.42
set #k=1
--In case the interests obtained at start of period then set #k as follows
--set #k = 1 + #rate
select Log10((-#Fv * (#Rate / #k) + #Pmt)
/ (#Pmt + (#Rate / #k) * #Pv))
/ Log10(1 + #Rate) as nper
+------------------+
| nper |
+------------------+
| 9.53201056406188 |
+------------------+
Excel
=NPER(15.99/100/12, -167.65,1491.42,0)
Here is a dbfiddle link
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=2f470cee443e10647f83d6e640129d51
For this use case this was my solution and it works perfect to NPER function in excel:
declare #fv float
declare #k float
set #fv=0
set #k=1
select CEILING(Log10((-#Fv * ((current_interest_rate/100/12) / #k) + -amortized_payment_amount)
/ (-amortized_payment_amount + ((current_interest_rate/100/12) / #k) * current_principal_balance))
/ Log10(1 + (current_interest_rate/100/12))) as nper

Random key generation using SQL Server

I am using below function to generate 5 letters random key but it is the combination of all letters and numbers.
But as per requirements I need to generate a random unique key that does not contain the characters 'o' and 'I', and also does not contain the numbers '0' and '1' .
DECLARE #automateKey VARCHAR(15)
DECLARE #Length INT = 6
DECLARE #Count INT = 2
SET #Length = #Length + 1
SET #Count = #Count + 1
SELECT #automateKey =
(SELECT CAST((ABS(Checksum(NewId())) % 10) AS VARCHAR(1)) +
CHAR(ascii('A')+(Abs(Checksum(NewId()))%25)) +
LEFT(newid(),#count) Random_Number)
SELECT (#automateKey)
I am not sure how I can escape the those particular characters and numbers from random key generation
Could anyone please help with this query? Many thanks in advance.
Use RAND() is better than NEWID(), as it is not random in nature.
DECLARE
#Chars varchar(100) = 'ABCDEFGHJKLMNPQRSTUVWXYZ',
#CharsAndNumbers varchar(100) = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'
SELECT
CONCAT
(
-- Numbers 2-9
FLOOR(RAND() * 8 + 2),
-- Any UPPER CASE character but not I,O
SUBSTRING(#Chars, CONVERT(int, RAND() * LEN(#Chars) + 1), 1),
-- Numbers 2-9 and Any UPPER CASE charcter but not I,O
SUBSTRING(#CharsAndNumbers, CONVERT(int, (RAND() * LEN(#CharsAndNumbers)) + 1), 1),
SUBSTRING(#CharsAndNumbers, CONVERT(int, (RAND() * LEN(#CharsAndNumbers)) + 1), 1),
SUBSTRING(#CharsAndNumbers, CONVERT(int, (RAND() * LEN(#CharsAndNumbers)) + 1), 1)
)
Testing SQL in StackExchange Data Explorer
I would suggest doing something like this:
declare #chars varchar(255);
set #chars = 'ABCDEFGHIJKLMNPQRSTUVWXYZ23456789';
declare #i int;
set #i = 1;
declare #automateKey = varchar(255);
set #automatekey = '';
while #i <= 5
begin
set #automateKey = #automateKey + substring(#chars, cast(rand() * len(#chars) + 1 as int), 1)
set #i = #i + 1;
end;
This solution has two main components. First, all the valid characters are defined as a string. Second, it uses a while loop to set the values using rand().
rand() behaves in a strange way in SQL Server -- it is evaluated only once for a given query when it appears. Hence, I generally do not want to use it in a SELECT statement.
I should add that the following can replace the WHILE loop:
set #automateKey = (substring(#chars, cast(rand() * len(#chars) + 1 as int), 1) +
substring(#chars, cast(rand() * len(#chars) + 1 as int), 1) +
substring(#chars, cast(rand() * len(#chars) + 1 as int), 1) +
substring(#chars, cast(rand() * len(#chars) + 1 as int), 1) +
substring(#chars, cast(rand() * len(#chars) + 1 as int), 1)
);
Your key seems to have 3 parts:
First character is random number 0-10 which is genereted with:
(Abs(Checksum(NewId()))%10)
Second character is random letter which is generated with:
CHAR(ascii('A')+(Abs(Checksum(NewId()))%25))
Third part is three characters that could be either letter or number
LEFT(newid(),#count)
It's not hard to change the first two parts to eliminate unwanted characters, but would require bit of changing to third part.
Instead you can just leave this as is, and add to the end while loop to search and replace unwanted characters - 0 and 1 with random number, O and L with random letter:
DECLARE #automateKey VARCHAR(15)
DECLARE #Length INT = 6
DECLARE #Count INT = 2
SET #Length = #Length + 1
SET #Count = #Count + 1
--this seems unnecessary , why not just SET #Count = 3 ?
SELECT #automateKey =
(SELECT CAST((ABS(Checksum(NewId())) % 10) AS VARCHAR(1)) +
CHAR(ascii('A')+(Abs(Checksum(NewId()))%25)) +
LEFT(newid(),#count) Random_Number)
WHILE (#automateKey LIKE '%0%' OR #automateKey LIKE '%1%')
BEGIN
SELECT #automateKey = REPLACE(#automateKey, '0', (Abs(Checksum(NewId()))%10))
SELECT #automateKey = REPLACE(#automateKey, '1', (Abs(Checksum(NewId()))%10))
END
WHILE (#automateKey LIKE '%O%' OR #automateKey LIKE '%L%')
BEGIN
SELECT #automateKey = REPLACE(#automateKey, 'O', CHAR(ascii('A')+(Abs(Checksum(NewId()))%25)))
SELECT #automateKey = REPLACE(#automateKey, 'L', CHAR(ascii('A')+(Abs(Checksum(NewId()))%25)))
END
SELECT #automateKey

What am I doing wrong in calculating cidr IP range?

I am trying to write a function that will output some address information on a CIDR formatted IP (output underneath code):
create function dbo.ConvertIpToInt (#Ip as varchar(15))
returns bigint
as
begin
return (convert(bigint, parsename(#Ip, 1)) +
convert(bigint, parsename(#Ip, 2)) * 256 +
convert(bigint, parsename(#Ip, 3)) * 256 * 256 +
convert(bigint, parsename(#Ip, 4)) * 256 * 256 * 256)
end
go
create function dbo.ConvertIntToIp (#Int bigint)
returns varchar(15)
as
begin
declare
#IpHex varchar(8)
,#IpDotted varchar(15)
select
#IpHex = substring(convert(varchar(30), master.dbo.fn_varbintohexstr(#Int)), 11, 8)
select
#IpDotted = convert(varchar(3), convert(int, (convert(varbinary, substring(#IpHex, 1, 2), 2)))) + '.' +
convert(varchar(3), convert(int, (convert(varbinary, substring(#IpHex, 3, 2), 2)))) + '.' +
convert(varchar(3), convert(int, (convert(varbinary, substring(#IpHex, 5, 2), 2)))) + '.' +
convert(varchar(3), convert(int, (convert(varbinary, substring(#IpHex, 7, 2), 2))))
return #IpDotted
end
go
create function dbo.GetCidrIpRange (#CidrIp varchar(15))
returns #result table
(
CidrIp varchar(15) not null,
Mask int not null,
LowRange varchar(15) not null,
LowIp varchar(15) not null,
HighRange varchar(15) not null,
HighIp varchar(15) not null,
AddressQty bigint not null
)
as
begin
declare #Base bigint = cast(4294967295 as bigint)
declare #Mask int = cast(substring(#CidrIp, patindex('%/%' , #CidrIP) + 1, 2) as int)
declare #Power bigint = Power(2.0, 32.0 - #Mask) - 1
declare #LowRange bigint = dbo.ConvertIpToInt(left(#CidrIp, patindex('%/%' , #CidrIp) - 1)) & (#Base ^ #Power)
declare #HighRange bigint = #LowRange + #Power
insert #result
select
CidrIp = #CidrIp
, Mask = #Mask
, LowRange = #LowRange
, LowIp = dbo.ConvertIntToIp(#LowRange)
, HighRange = #HighRange
, HighIp = dbo.ConvertIntToIp(#HighRange)
, AddressQty = convert(bigint, power(2.0, (32.0 - #Mask)))
return
end
go
select * from dbo.GetCidrIpRange('195.65.254.11/2');
This outputs the following:
CidrIp Mask LowRange LowIp HighRange HighIp AddressQty
--------------------------------------------------------------------------------------
195.65.254.11/2 2 3221225472 192.0.0.0 4294967295 255.255.255.255 1073741824
I have been browsing SO and Google for some hours now, and I am quite convinced that ConvertIpToInt and ConvertIntToIp are correct.
However, I was expecting the following output:
CidrIp Mask LowRange LowIp HighRange HighIp AddressQty
--------------------------------------------------------------------------------------
195.65.254.11/2 2 3275881985 195.65.254.1 3275882238 195.65.254.254 254
Can someone please point me out where the mistake in my code is? I've been staring myself blind and I don't see it (or I am misunderstanding how to do this).
According to both http://www.ipaddressguide.com/cidr and http://jodies.de/ipcalc?host=195.65.254.11&mask1=2&mask2=, your calculations are correct. The only disagreement between those two sites is that the jodies.de/ipcalc page removes the lowest and highest (broadcast) IP addresses from the range.
I tested with both 195.65.254.11/2 and 195.65.254.11/24. In order to get your code working, I needed to change the input parameter specification on dbo.GetCidrIpRang to be VARCHAR(20) (as mentioned by #Damien_The_Unbeliever in a comment on the question).
Two notes regarding performance:
For the ConvertIpToInt and ConvertIntToIp Scalar UDFs you might be better off using the INET_AddressToNumber and INET_NumberToAddress functions, respectively, that are included in the Free version of the SQL# SQLCLR library (which I wrote, but hey, Free :). The reason for this recommendation is that unlike T-SQL UDFs, deterministic SQLCLR UDFs (and these two are) do not prevent parallel plans.
If you don't want to go the SQLCLR route, then you should, at the very least, keep the ConvertIntToIp function as purely mathematical. There is no reason to do all of those conversions and substrings.
CREATE FUNCTION dbo.IPNumberToAddress(#IPNumber BIGINT)
RETURNS VARCHAR(15)
WITH SCHEMABINDING
AS
BEGIN
DECLARE #Oct1 BIGINT,
#Oct2 INT,
#Oct3 INT;
SET #Oct1 = #IPNumber / (256 * 256 * 256);
SET #IPNumber -= (#Oct1 * (256 * 256 * 256));
SET #Oct2 = #IPNumber / (256 * 256);
SET #IPNumber -= (#Oct2 * (256 * 256));
SET #Oct3 = #IPNumber / 256;
SET #IPNumber -= (#Oct3 * 256);
RETURN CONCAT(#Oct1, '.', #Oct2, '.', #Oct3, '.', #IPNumber);
END;
GO
And then:
SELECT dbo.IPNumberToAddress(3275881995);
-- 195.65.254.11
For the GetCidrIpRange TVF, you would be better off converting that to be an Inline TVF. You can accomplish the multi-step calculations via CTEs in the following manner (you will just need to clean it up a little / finish it):
WITH cte1 AS
(
SELECT 2 AS [Mask] -- replace with real formula
), cte2 AS
(
SELECT 999 AS [Base], -- replace with real formula
POWER(2.0, 32.0 - cte1.[Mask]) - 1 AS [Power],
cte1.[Mask]
FROM cte1
), cte3 AS
(
SELECT SQL#.INET_AddressToNumber(left(#CidrIp, PATINDEX('%/%' , #CidrIp) - 1))
& (cte2.[Base] ^ cte2.[Power]) AS [LowRange],
cte2.[Power],
cte2.[Mask]
FROM cte2
)
SELECT #CidrIp AS [CidrIp],
cte3.[Mask],
cte3.[LowRange],
SQL#.INET_NumberToAddress(cte3.[LowRange]) AS [LowIp],
(cte3.[LowRange] + cte3.[Power]) AS [HighRange],
SQL#.INET_NumberToAddress(cte3.[LowRange] + cte3.[Power]) AS [HighIp],
CONVERT(BIGINT, POWER(2.0, (32.0 - cte3.[Mask]))) AS [AddressQty]
FROM cte3 c;

Error in MDW script from SQL tracer

SQL Server MDW comes with built-in reports and I was curious about the underlying code behind the report to customize the reports. I used sql tracer to see what happens and this was the output containing errors. Was wondering if anyone could see what was wrong?
I get the following error:
Msg 4145, Level 15, State 1, Line 26 An expression of non-boolean type
specified in a context where a condition is expected, near ';'.
DECLARE #ServerName varchar(max)
DECLARE #SelectedDatabaseName varchar(max)
DECLARE #snapshot_id int
SELECT TOP 1 #snapshot_id = snapshot_id FROM (
SELECT DISTINCT TOP 100 d.snapshot_id
FROM snapshots.disk_usage d, core.snapshots ss
WHERE ss.instance_name = #ServerName
AND ss.snapshot_id = d.snapshot_id
ORDER BY d.snapshot_id DESC
) AS q
ORDER BY snapshot_id ASC
SELECT
database_name,
CONVERT (datetime, SWITCHOFFSET (CAST (d.collection_time AS datetimeoffset), '+00:00')) AS collection_time,
d.snapshot_id,
((convert(dec (15,2),d.dbsize) + convert(dec(15,2),d.logsize)) * 8192 / 1048576.0) AS 'database_size_mb',
'reserved_mb' = (d.reservedpages * 8192 / 1048576.0),
'data_mb' = convert(dec(15,2),d.pages) * 8192 / 1048576.0,
'index_mb'= (d.usedpages - d.pages) * 8192 / 1048576.0,
'unused_mb' = ((convert(dec (15,2),d.reservedpages) - convert(dec(15,2),d.usedpages)) * 8192 / 1048576.0),
'unallocated_space_mb' = (
case
when d.dbsize >= d.reservedpages
then (convert (dec (15,2),d.dbsize) - convert (dec (15,2),d.reservedpages)) * 8192 / 1048576.0
else 0
end)
FROM snapshots.disk_usage d, core.snapshots ss
WHERE database_name =#SelectedDatabaseName
AND d.snapshot_id >= #snapshot_id
AND ss.instance_name = #ServerName
AND d.snapshot_id = ss.snapshot_id
ORDER BY collection_time asc

Getting RGB(R,G,B) from ARGB integer (SQL)

I need to display some data in an SSRS 2008r2 report and the colors have to match a Windows VB app that saves it's colors as integers (e.g.16744703 is a pinkish color). I believe this is ARGB format. I'm not concerned about the alpha value, as the application does not allow the user to modify it.
I'm stuck on the SQL to convert ARGB to something compatible in SSRS. I need to do the translation in SQL as there are other factors that may override an objects color.
I can work with 3 ints for rgb or a hex value
Anyone got any idea how tot do this?
Regards
mark
Figured it out. Here's a function that returs either RGB() or Hex
-- Description: Converts ARGB to RGB(RR,GG,BB)
-- e.g. 16744703 returns RGB(255,128,255) or #FF80FF
CREATE FUNCTION [dbo].[ARGB2RGB]
(
#ARGB AS BIGINT
,#ColorType AS VARCHAR(1) -- 'H' = Hex, 'R' = RGB
)
RETURNS VARCHAR(16)
AS
BEGIN
DECLARE #Octet1 TINYINT
DECLARE #Octet2 TINYINT
DECLARE #Octet3 TINYINT
DECLARE #Octet4 TINYINT
DECLARE #RestOfColor BIGINT
SET #Octet1 = #ARGB / 16777216
SET #RestOfColor = #ARGB - ( #Octet1 * CAST(16777216 AS BIGINT) )
SET #Octet2 = #RestOfColor / 65536
SET #RestOfColor = #RestOfColor - ( #Octet2 * 65536 )
SET #Octet3 = #RestOfColor / 256
SET #Octet4 = #RestOfColor - ( #Octet3 * 256 )
RETURN
CASE #ColorType
WHEN 'R'
THEN 'RGB(' + CONVERT(VARCHAR, #Octet4) + ','
+ CONVERT(VARCHAR, #Octet3) + ',' + CONVERT(VARCHAR, #Octet2)
+ ')'
WHEN 'H'
THEN '#' + RIGHT(sys.fn_varbintohexstr(#Octet4), 2)
+ RIGHT(sys.fn_varbintohexstr(#Octet3), 2)
+ RIGHT(sys.fn_varbintohexstr(#Octet2), 2)
END
END
Hope someone else finds it useful
Regards
Mark
create FUNCTION [dbo].[ConvertRGB]
(
#ARGB AS float
)
RETURNS #ReturnValue TABLE ( R TINYINT,B TINYINT, G TINYINT )
as
BEGIN
DECLARE #testvarbinary binary(4)
DECLARE #strRBG nvarchar(MAX)
set #testvarbinary = CONVERT(binary(4),#ARGB)
set #strRBG=( SELECT substring(sys.fn_varbintohexstr(#testvarbinary),5,6))
DECLARE #temp AS TABLE (hex char(6))
INSERT INTO #temp
VALUES (#strRBG)
DECLARE #strHex AS varchar(16)
SET #strHex = '0123456789abcdef' -- Assuming case-insensitive collation!
INSERT INTO #ReturnValue
( R,G,B )
SELECT 16 * (CHARINDEX(SUBSTRING(hex, 1, 1), #strHex) - 1) + (CHARINDEX(SUBSTRING(hex, 2, 1), #strHex) - 1)
,16 * (CHARINDEX(SUBSTRING(hex, 3, 1), #strHex) - 1) + (CHARINDEX(SUBSTRING(hex, 4, 1), #strHex) - 1)
,16 * (CHARINDEX(SUBSTRING(hex, 5, 1), #strHex) - 1) + (CHARINDEX(SUBSTRING(hex, 6, 1), #strHex) - 1)
FROM #temp
RETURN
END;
GO
--select * from [ConvertRGB](10592513)
I was looking for doing something like this and came up with this post. It was of great help but I found it more appealing doing it inline as follow. Notice that the result can be used inside the SSRS Color Expression with no additional conversions
select CustomerID
,SSRSColor = '#' + SUBSTRING(S.TXTHEXColor, 5, 2)
+ SUBSTRING(S.TXTHEXColor, 3, 2)
+ SUBSTRING(S.TXTHEXColor, 1, 2)
from
(
Select CustomerID
,[TXTHEXColor] = right(sys.fn_varbintohexstr(CONVERT(varbinary, T.Color)), 6)
From SomeTable T
) S
If a function is still needed then following is a much shorter way but bear in mind that with big record sets functions can slow down the process a lot.
CREATE FUNCTION [dbo].[SSRSColor] (
#ARGB AS INT
)
RETURNS VARCHAR(7)
AS
BEGIN
DECLARE #TXTHEXColor varchar(100)
Select #TXTHEXColor = right(sys.fn_varbintohexstr(CONVERT(varbinary, #ARGB)), 6)
return '#' + SUBSTRING(#TXTHEXColor, 5, 2) + SUBSTRING(#TXTHEXColor, 3, 2) + SUBSTRING(#TXTHEXColor, 1, 2)
END