Calculate Average of boolean values - sql

I have seen a few examples but not many to boolean and been trying for hours and I cant get this to compile. I have a column called Status. In it is either 1 for true or 0 for false. I would just like to calculate the average percentage of uptime over downtime but keep hitting a wall.
SET ARITHABORT OFF;
SET ANSI_WARNINGS OFF;
declare #uptime float
declare #downtime float
declare #average float
Set #uptime = (select count(Status) AS Online from dbo.[Uptimes]
where Status=1)
print #uptime
Set #downtime = (select count(Status) AS Offline from dbo.[Uptimes]
where Status=0)
Select #uptime, CASE #downtime WHEN 0 THEN 100 ELSE #uptime / #downtime * 100
end

Was able to modify the last statement to be a correct syntax using this format
SET ARITHABORT OFF
SET ANSI_WARNINGS OFF
declare #uptime float
declare #downtime float
declare #uptimeavg float
Set #uptime = (select count(Status) AS Online from dbo.[Uptimes]
where Status=1)
Set #downtime = (select count(Status) AS Offline from dbo.[Uptimes]
where Status=0)
Set #uptimeavg = (#uptime / #downtime * 100)
Select (CASE WHEN #downtime = 0 THEN 100 ELSE #uptimeavg END);
End

Related

Scalar-valued function returning NULL

I have the below function, and for the life of me, I cannot get it to return a value, I get NULL every time.
I am calling it via select [dbo].[getFiatProfit](600.26,'GBP', 1000.99,'BTC') as op
What am I missing?
/****** Object: UserDefinedFunction [dbo].[getFiatProfit] Script Date: 06/07/2022 11:42:26 ******/
ALTER FUNCTION [dbo].[getFiatProfit] (
#fiatInvested float,
#fiatInvestedCurrency nvarchar,
#quantity float,
#currency nvarchar
)
RETURNS float
AS
BEGIN
declare #tmp float
declare #result float
declare #usdtgbp float
IF (#fiatInvestedCurrency = 'USD')
BEGIN
select #tmp = [dbo].[usdtPairs].[Value] from [dbo].[usdtPairs] where usdtPairs.ID = #currency;
select #usdtgbp = [dbo].[usdtPairs].[Value] from [dbo].[usdtPairs] where usdtPairs.ID = 'GBP';
set #result = (((#quantity * #tmp) - #fiatInvested) / #usdtgbp);
-- set #result = #quantity * #tmp;
END
ELSE
BEGIN
select #tmp = [dbo].[usdtPairs].[Value] from [dbo].[usdtPairs] where usdtPairs.ID = #currency;
set #result = ((#quantity * #tmp) - #fiatInvested);
-- set #result = #quantity * #tmp;
END
return (#result)
END
Your issue looks it's because your parameters are declared without a length. nvarchar defaults to a length of 1 in a lot of circumstances, so it's simply the wrong value being received. A much better data type would be char(3) which is fixed length, given that all currencies have exact three-letter names.
You should also convert this function into a Table Valued Function, which is likely to perform far better.
CREATE OR ALTER FUNCTION dbo.getFiatProfit (
#fiatInvested float,
#fiatInvestedCurrency char(3),
#quantity float,
#currency char(3)
)
RETURNS TABLE
AS RETURN
SELECT
result = ((#quantity * u.Value) - #fiatInvested)
/ (CASE WHEN #fiatInvestedCurrency = 'USD'
THEN 1
ELSE
(SELECT u2.Value FROM dbo.usdtPairs u2 WHERE u2.ID = 'GBP')
END)
FROM dbo.usdtPairs u
WHERE u.ID = #currency;
You use it like this
SELECT t.*, fp.*
FROM YourTable t
CROSS APPLY dbo.getFiatProfit(t.fiatInvested, t.fiatInvestedCurrency, t.Qty, 'GBP') fp;

how to transfer this iterative solution to a set-based solution

/*
Stored Procedure
April 30, 2021
Mohamad Chaker
*/
USE CIS111_BookStoreMC
GO
--drop the procedure
IF OBJECT_ID('spAssetInfo') IS NOT NULL
DROP PROC spAssetInfo
GO
--Create the stored procedure
CREATE PROC spAssetInfo
AS
--Create a temporary table to display the inventory
SELECT AssetID, Description, Cost, PurchaseDate
INTO #temptable
FROM Assets
--Add a new column to the temporary table displaying the date an asset is completely depreciated
ALTER TABLE #temptable ADD CurrentValue MONEY
DECLARE #years INT;
DECLARE #currentYear INT;
SET #currentYear = YEAR(getdate());
DECLARE #cost MONEY;
--Add a new column to the temporary table to display the current value of the item in the current year
ALTER TABLE #temptable ADD CompleteDepreciationYear DATE
--#value holds the cost of an asset and is used to check when the value of an asset drops low
DECLARE #value MONEY;
SET #value = 0.00;
--#counter is an int that I used to iterate over the table rows
DECLARE #counter INT;
--#depreciationNum holds the amount of years until an item is completely depreciated to be later used in DATEADD()
DECLARE #depreciationNum INT;
SET #counter = 1;
DECLARE #assetsTableSize INT;
SET #assetsTableSize = (SELECT COUNT(*) FROM Assets);
WHILE (#counter <= #assetsTableSize)
BEGIN
--Current Value
SET #years = #currentYear - (select YEAR(PurchaseDate) From Assets Where AssetID = #counter);
SET #cost = (select Cost From Assets Where AssetID = #counter);
--calculate current value of each asset
WHILE(#years>0)
BEGIN
SET #cost = #cost * 0.8;
SET #years = #years - 1;
END
--add the current value of each asset to the temporary table
UPDATE #temptable
SET CurrentValue = #cost
WHERE AssetID = #counter;
--Deprection Year
SET #depreciationNum = 0;
SET #value = (select Cost From Assets Where AssetID = #counter);
WHILE(#value >0.1)
BEGIN
SET #value = #value * 0.8;
SET #depreciationNum = #depreciationNum + 1;
END
--add the date each asset is completely depreciated to the temporary table
UPDATE #temptable
SET CompleteDepreciationYear = CAST(DATEADD(year, #depreciationNum, (select PurchaseDate From Assets Where AssetID = #counter)) AS DATE)
WHERE AssetID = #counter;
--increment the counter
SET #counter = #counter + 1;
END
--display the assets inventory
SELECT * FROM #temptable
Prompt: Show an asset inventory along with the current value (minus 20% depreciation per year). Also show the year when each item will be
completely depreciated.
basically I am trying to show an Asset Inventory with PurchaseDate and the date the item is completely depreciated, the asset depreciates 20% per year. I tried to do a temporary table and copy some of the assets table columns to it then adding a column for the date when the asset completely depreciates.
I implemented this using an iterative solution but I was advised to post on SO to try and do this using a set-based implementation. I am new to SQL and newly learned that it's a set-based language and that it isn't very good with iterative solutions.
Thank you in advance!
The formula to calculate the current value is:
CurrentValue = InitialValue * (1-0.2)^(Today - InitialDate)
To find the NumberOfYears that the asset will reach 0.1:
0.1 = CurrentValue * (1-0.2)^NumberOfYears. Taking the log from both sides:
Log(0.1) = Log(CurrentValue * Log(0.8^NumberOfYears)
Log(0.1) = Log(CurrentValue) + Log(0.8^NumberOfYears)
Log(0.1) = Log(CurrentValue) + NumberOfYears * Log(0.8)
[Log(0.1) - Log(CurrentValue)] / Log(0.8) = NumberOfYears
You can create a function that use this formula and returns the NumberOfYears:
CREATE FUNCTION GetNumberOfYears(
#InitialValue FLOAT
)
RETURNS FLOAT
AS
BEGIN
DECLARE #CurrentValue FLOAT,
#InitialValue FLOAT,
#YearsToZero FLOAT;
-- CurrentValue Formula
SET #CurrentValue = #InitialValue * POWER(CAST(0.8 AS FLOAT), YEAR(GETDATE() - YEAR(PurchaseDate));
-- NumberOfYears Formula
SELECT #YearsToZero = (LOG(0.1) - LOG(#CurrentValue)) / LOG(0.8);
RETURN #YearsToZero;
END
It will give you the number of years (e.g. 54.6724159 years).
Then use it like SELECT GetNumberOfYears(Cost) FROM Assets
Or, you can use directly from your SELECT:
SELECT
Cost * POWER(CAST(0.85 AS FLOAT), YEAR(GETDATE()) - YEAR(PurchaseDate)) AS CurrentValue,
(LOG(0.1) - LOG( [the above CurrentValue formula] )) / LOG(0.8) AS YearsToZero
FROM Assets

SQL: Use table from table valued function

I'm sure there is a better way of doing this as I am running the same Table Valued Function several times, is there a way to get the table from the TVF just once and then run the queries against it to set my variables?
DECLARE #RUNS INT
DECLARE #WON INT
DECLARE #PERC FLOAT
SET #RUNS = (SELECT COUNT(*) FROM tvf_horse_going('Allow Me', 'HEAVY'))
SET #WON = (SELECT COUNT(*) FROM tvf_horse_going('Allow Me', 'HEAVY') WHERE PosNum = '1')
SET #PERC = (#WON * 100) / #RUNS
SELECT #RUNS As Runs, #WON As Won, #PERC As Perc
select
#RUNS = count(*),
#WON = sum(case when PosNum = 1 then 1 else 0 end)
from tvf_horse_going('Allow Me', 'HEAVY')
set #PERC = (#WON * 100) / #RUNS
SELECT runs
, won
, won * 100.0 / runs As perc
FROM (
SELECT Count(*) As runs
, Sum(CASE WHEN PosNum = 1 THEN 1 ELSE 0 END) As won
FROM tvf_horse_going('Allow Me', 'HEAVY')
) As results
Note the * 100.0. This is a quick and easy way to avoid using integer math. Other [less lazy] methods would include: Cast(won As decimal(5,2)) / Cast(runs As decimal(5,2)) or similar
You can also use hash table (temporary table).
DECLARE #RUNS INT
DECLARE #WON INT
DECLARE #PERC FLOAT
SELECT * INTO #TEMP FROM tvf_horse_going('Allow Me', 'HEAVY')
SET #RUNS = (SELECT COUNT(*) FROM #TEMP)
SET #WON = (SELECT COUNT(*) FROM #TEMP WHERE PosNum = '1')
SET #PERC = (#WON * 100) / #RUNS
SELECT #RUNS As Runs, #WON As Won, #PERC As Perc

Cumulative Summation in SQL Server

I'm writing a query, part of Stored Procedure in SQL Server. I need to find cumulative summation in SQL Server.
A variable will hold a integer value say 100. Let's say
Declare #Variable int = 100
Now the #NewVariable will have below formula:
#NewVariable = #Variable * (1 - 0.005)
Hence #NewVariable = 99.5
Now, the #NewestVariable will have below formula;
#NewestVariable = #NewVariable * (1 - 0.005)
Hence the #NewestVariable will have value of 99.00
Likewise this calculation will occur 24 times and all the results will be sum at the end.
Hence final result will be: 100 + 99.5 +99.00 + .... ...
I tried to achieve the desired result using #Count Variable (DECLARE #COUNT INT = 24) and using a While loop, but I'm not sure whether I'm correct or not?
Request your help!
Thanks!
You can do it using a CTE as below:
declare #variable int = 100
;with cte as
(
select convert(numeric(8,2), #variable) as var, 1 as recCount
union all
select convert(numeric(8,2), #variable * (1 - recCount*0.005)) as var, recCount+1 as recCount
from cte
where recCount < 24
)
select sum(var) as total from cte
Working Fiddle Demo
Edit: Adjusted to resolve rounding error as per #peter.petrov 's comment
If you need to get the values of each row before counting, please use this fiddle
Try this code.
declare #Variable decimal(20,10);
set #Variable = 100;
declare #Sum decimal(20,10);
set #Sum = 0;
declare #cnt int;
set #cnt = 1;
while (#cnt <= 24)
begin
set #Sum = #Sum + #Variable;
set #Variable = #Variable * (1.0 - 0.005);
set #cnt = #cnt + 1;
end;
select #Sum;
A simple answer would be like this
DECLARE #Variable INT = 100
SELECT #Variable
DECLARE #counter INT = 1
DECLARE #SumVariable NUMERIC(20,2) = #Variable
DECLARE #NewVariable NUMERIC(20,2) = #Variable
WHILE(#counter<24)
BEGIN
SET #NewVariable = #NewVariable * 0.995
SET #SumVariable = #SumVariable + #NewVariable
SET #counter = #counter+1
END
SELECT #SumVariable
You could also use a "quirky update" to avoid while-loops. If you're not interesed in intermediate resuls, just modify the last code line to select only the max(total).
DECLARE #total DECIMAL(10,3) = 0.000
;WITH "data" AS
(
SELECT CAST( 100 AS DECIMAL(10,3)) AS id
UNION ALL
SELECT CAST( id * ( 1 - 0.005 ) AS DECIMAL(10,3)) FROM "data" WHERE id > 100 * (1 - 21 * 0.005)
)
SELECT
id, total = CAST( 0.000 AS DECIMAL(10,3))
INTO #temp
FROM
"data"
OPTION ( MAXRECURSION 23 );
UPDATE t SET #total = total = #total + id FROM #temp t
SELECT * FROM #temp
See SQL-Fiddle for testing.

How to set the value of local variable inside else loop in SQL Server 2008

I am trying to do a select from multiple tables and some of the columns values will be based on the case like this (the statement in else loop is giving error)
(select case
when #caQuoteIdTemp = corporateQuotes.caQuoteID
then #randomNumber
else (set #caQuoteIdTemp = corporateQuotes.caQuoteID )
end )as costmoney
here #caQuoteIdTemp and #randomNumber are local variables I declared like this -
DECLARE #randomNumber FLOAT
declare #caQuoteIdTemp int
set #randomNumber = convert(numeric(15,0),rand() * 899999999999999) + 100000000000000
set #caQuoteIdTemp = 1
Now my questions is: how can I set the value of #caQuoteIdTemp to #caQuoteIdTemp = corporateQuotes.caQuoteID in the else statement?
At the moment I am getting syntax error. I am wondering if it is not at all possible to change the value of a variable
EDIT:::
The full Query is like this
DECLARE #randomNumber FLOAT
declare #caQuoteIdTemp int
set #randomNumber = convert(numeric(15,0),rand() * 899999999999999) + 100000000000000
set #caQuoteIdTemp = 1
select
corporateQuotes.caQuoteID,
prod_sub.ProdID,
corporateQuoteItems.prodSubID,
prod.publisherID,
prod.Title,
prod.code,
corporateQuotes.date_ordered,
(select DATEPART(WK, corporateQuotes.date_ordered)) as week_ordered,
(select DATEPART(MONTH, corporateQuotes.date_ordered)) as month_ordered,
(select DATEPART(YEAR, corporateQuotes.date_ordered)) as year_ordered,
prod_sub.[length],
prod_sub.issues,
prod_sub.extra_info,
-- cost money --
(select case
when corporateQuotes.discountIsPoundValue = 1
then prod_sub.your_price + prod_sub.commission - corporateQuotes.discount
else (prod_sub.your_price + prod_sub.commission - (corporateQuotes.discount * prod_sub.your_price/100))
end as costmoney)as costmoney,
corporateAccounts.companyName,
-- saving money --
(prod_sub.rrp * prod_sub.issues - prod_sub.your_price )as savingmoney,
corporateAccounts.title,
corporateAccounts.firstName,
corporateAccounts.lastName,
corporateAccounts.street,
corporateAccounts.suburb,
corporateAccounts.[state],
corporateAccounts.postcode,
corporateAccounts.country,
corporateAccounts.phone,
-- Do something about TransID here--
(select #caQuoteIdTemp =(
case
when #caQuoteIdTemp = corporateQuotes.caQuoteID
then #randomNumber
else corporateQuotes.caQuoteID
as test) ,
#randomNumber as st_transId,
prod_sub.start_issue,
prod_sub.effort_code,
prod_sub.premium_code,
--do something about commison --
-- discount applied --
(select
case
when corporateQuotes.discountIsPoundValue = 1
then corporateQuotes.discount
else (corporateQuotes.discount * prod_sub.your_price/100)
end as discountApplied)as discountApplied,
-- pending orderID --
--PP transID --
-- commison --
corporateQuotes.commission,
corporateQuotes.discount
from corporateQuotes
join corporateQuoteItems on
corporateQuotes.caQuoteID = corporateQuoteItems.caQuoteID
join corporateAccounts on
corporateQuotes.caID = corporateAccounts.caID
join prod_sub on
corporateQuoteItems.prodSubID = prod_sub.ProdSubID
join prod on
prod_sub.ProdID = prod.ProdID
where corporateQuotes.orderId is null
--group by corporateQuotes.caQuoteID
order by corporateQuotes.caQuoteID
;
What you have is a select statement but in the else part all you are doing is setting a variable. I'm assuming you want to select #randomNumber if the Id's match otherwise just reset the variable in which case why not use an IF...ELSE... e.g.
IF (EXISTS (SELECT * FROM corporateQuotes WHERE #caQuoteIdTemp = corporateQuotes.caQuoteID))
SELECT #randomNumber AS costmoney
ELSE
SET #caQuoteIdTemp = corporateQuotes.caQuoteID
DECLARE #randomNumber FLOAT
declare #caQuoteIdTemp int
set #randomNumber = convert(numeric(15,0),rand() * 1000) + 1000
set #caQuoteIdTemp = 1
select #caQuoteIdTemp =(case when #caQuoteIdTemp = corporateQuotes.caQuoteID
then #randomNumber
else corporateQuotes.caQuoteID
end ) from corporateQuotes
SELECT #caQuoteIdTemp as costmoney
i have reduced the range for random number, previous values were causing arithmetic overflow.