SQL: Use table from table valued function - sql

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

Related

I want to make two Queries into one by a variable

I want to make two queries into one by a variable
ALTER PROCEDURE [dbo].[Balance]
#userId nvarchar(200)
AS
BEGIN
DECLARE #parchesQueryAdd decimal(18,2),#parchesQueryRemove decimal(18,2), #topupQuery decimal(18,2), #Balance decimal(18,2), #totalamount decimal(18,2)
/****** this two Querys starts ******/
SET #parchesQueryAdd = (SELECT SUM(Amount * CurrentBalanceCurrency) from UserBalance where BalanceForId = #userId and AmountType = 10)
SET #parchesQueryRemove = (SELECT SUM(Amount * CurrentBalanceCurrency) from UserBalance where BalanceForId = #userId and AmountType = 20)
/****** End ******/
SET #Balance = #parchesQueryAdd - #parchesQueryRemove
SET #topupQuery = (SELECT SUM(Amount * Quentity) from TopUpRecords where TopupById = #userId)
SET #totalamount= #Balance - #topupQuery
PRINT #totalamount
END
you could just use a single query for both the sum or the total
select sum( case when AmountType = 10
then Amount * CurrentBalanceCurrency else 0 end ) parchesQueryAdd
, sum( case when AmountType = 20
then Amount * CurrentBalanceCurrency else 0 end ) parchesQueryRemove
, sum( case when AmountType = 10
then Amount * CurrentBalanceCurrency else 0 end ) -
sum( case when AmountType = 20
then Amount * CurrentBalanceCurrency else 0 end ) totQuery
from UserBalance where BalanceForId = #userId
You can use condition aggregate function to set the #Balance instead of two query.
DECLARE
#parchesQueryAdd decimal(18,2),
#parchesQueryRemove decimal(18,2),
#topupQuery decimal(18,2),
#Balance decimal(18,2),
#totalamount decimal(18,2)
SELECT #Balance = SUM(CASE WHEN AmountType = 10 THEN Amount * CurrentBalanceCurrency ELSE 0 END)
- SUM(CASE WHEN AmountType = 20 THEN Amount * CurrentBalanceCurrency ELSE 0 END)
FROM UserBalance
WHERE BalanceForId = #userId
GROUP BY BalanceForId
SET #topupQuery = (SELECT SUM(Amount * Quentity) from TopUpRecords where TopupById = #userId)
SET #totalamount= #Balance - #topupQuery
PRINT #totalamount
From my understanding,
create table #oneVariable (type int, amt int, bal int)
insert #oneVariable values(10,10,20),(10,10,20),(20,10,20),(30,10,20)
select type, sum(amt*bal) sumOfAmt_Bal from #oneVariable group by type
-- type 10 has amt*bal 400. And, type 20 has 200.
-- So according by you formula, Balance will be 200.
-- Whereas, type 30 has been eliminated.
declare #balance int
SET #Balance = (
select sum(
case type
when 10 then amt * bal
when 20 then -(amt * bal)
end
) sumOfAmt_Bal
from #oneVariable
)
print #balance

Difference between 2 numbers and return a %

I'm Looking to see if I can return a % from the following, but it need finishing.
SELECT COUNT(*) FROM VEHICLES_FW
WHERE ARCHIVE_STATUS_FW = 'N'
AND LOCATION_CODE_FW IN ('L0078')
This brings me back the amount of vehicle on this location. I know that the location has a limit of 180.
How do I write in so it works out that the % is 31%?
Ok, how i don't know what SQL variant you use, I write my response how I'll write on SQL Server.
If you want % respect of limit (180)
DECLARE #PERCENTAGE DECIMAL --Or INT if you prefer it
SET #PERCENTAGE = (SELECT COUNT(*) FROM VEHICLES_FW
WHERE ARCHIVE_STATUS_FW = 'N'
AND LOCATION_CODE_FW IN ('L0078')) * (100 / 180)
RETURN PERCENTAGE;
If you want % respect all cars
DECLARE #PERCENTAGE DECIMAL --Or INT if you prefer it
DECLARE #TOTAL DECIMAL --Or INT if you prefer it
SET #TOTAL = (SELECT COUNT(*) FROM VEHICLES_FW)
SET #PERCENTAGE = (SELECT COUNT(*) FROM VEHICLES_FW
WHERE ARCHIVE_STATUS_FW = 'N'
AND LOCATION_CODE_FW IN ('L0078')) * (100 / 180)
RETURN PERCENTAGE;

T-SQL Recursive Division

How do I perform recursive division in SQL?
I know the basic premise is
declare #i = 0
declare #testValue bigint = FLOOR(#num/#dem)
while #testValue>0
begin
set #num = #num - #testValue*#dem
set #testValue = FLOOR(#num/#dem)
set #i = #i + 1
end
, but I do not know how to do this in an efficient manner.
Your query can be rewritten as the following:
Declare #Num BigInt = 10000,
#Dem BigInt = 50
;With Cte As
(
Select Floor(#num / #dem) TestValue, 1 As Iteration
Union All
Select Floor((TestValue * #Dem) / #Dem) As TestValue, Iteration + 1
From Cte
Where TestValue > 0
)
Select *
From Cte
But there's a problem with the design of it. It multiplies the value back to the division result, and divides again, resulting in an infinite loop.
I believe this is what you were trying to do:
Declare #Num BigInt = 10000,
#Dem BigInt = 50
;With Cte As
(
Select Floor(#num / #dem) TestValue, 1 As Iteration
Union All
Select Floor(TestValue / #Dem) As TestValue, Iteration + 1
From Cte
Where TestValue > 0
)
Select *
From Cte
Which, in this case gives the following results:
TestValue Iteration
200 1
4 2
0 3

Performing a sub query alternatives

I'm looking for advice on how to get around the
"Cannot perform an aggregate function on an expression containing an
aggregate or a subquery".
On the Select PlateID From #Instances code, in the example below. Does anyone have any ideas or suggestions?
DECLARE #Instances AS TABLE(PlateID INT);
INSERT INTO #Instances(PlateID)VALUES(11638),(11637),(11632),(11659)
DECLARE #NumberofPlates INT;
SELECT #NumberofPlates = COUNT(*) FROM #Instances;
SELECT Instance_Plate_Room_Instance_ID_LNK
from dbo.M_Instance_Plate
WHERE Instance_Plate_Deleted = 0
group by Instance_Plate_Room_Instance_ID_LNK
having sum(case
when Instance_Plate_Plate_ID_LNK not in (SELECT PlateID
FROM #Instances)
then 1 else 0 end) = 0 and
SUM(case
when Instance_Plate_Plate_ID_LNK in (SELECT PlateID
FROM #Instances)
then 1 else 0 end) = #NumberofPlates;
In the absence of the structure of the physical table you included in your query I mocked up some random data for this, and put together a query that seems to work?
DECLARE #Instances AS TABLE(PlateID INT);
INSERT INTO #Instances(PlateID) VALUES (11638),(11637),(11632),(11632);
DECLARE #M_Instance_Plate TABLE (Instance_Plate_Plate_ID_LNK INT, Instance_Plate_Deleted INT, Instance_Plate_Room_Instance_ID_LNK INT);
INSERT INTO #M_Instance_Plate SELECT 11638, 0, 100;
INSERT INTO #M_Instance_Plate SELECT 11637, 0, 100;
INSERT INTO #M_Instance_Plate SELECT 11632, 0, 100;
INSERT INTO #M_Instance_Plate SELECT 11632, 0, 200;
INSERT INTO #M_Instance_Plate SELECT 11632, 1, 300;
DECLARE #NumberofPlates INT;
SELECT #NumberofPlates = COUNT(*) FROM #Instances;
WITH x AS (
SELECT
Instance_Plate_Room_Instance_ID_LNK,
SUM(CASE WHEN Instance_Plate_Plate_ID_LNK IS NULL THEN 1 ELSE 0 END) AS test_1, --Any missing
SUM(CASE WHEN Instance_Plate_Plate_ID_LNK IS NOT NULL THEN 1 ELSE 0 END) AS test_2 --Has coverage
FROM
#M_Instance_Plate ip
LEFT JOIN #Instances i ON i.PlateID = ip.Instance_Plate_Plate_ID_LNK
WHERE
Instance_Plate_Deleted = 0
GROUP BY
Instance_Plate_Room_Instance_ID_LNK)
SELECT
Instance_Plate_Room_Instance_ID_LNK
FROM
x
WHERE
test_1 = 0
AND test_2 = #NumberofPlates;
INSERT INTO #Instances(PlateID)VALUES(11638),(11637),(11632),(11659)
--DECLARE #NumberofPlates INT;
--SELECT #NumberofPlates = COUNT(*) FROM #Instances;
SELECT Instance_Plate_Room_Instance_ID_LNK
FROM dbo.M_Instance_Plate p
WHERE Instance_Plate_Deleted = 0
AND NOT EXISTS (
SELECT 1
FROM #Instances i
LEFT JOIN M_Instance_Plate m ON i.PlateID = m.Instance_Plate_Plate_ID_LNK
WHERE m.Instance_Plate_Room_Instance_ID_LNK = p.Instance_Plate_Room_Instance_ID_LNK
AND m.Instance_Plate_Plate_ID_LNK IS NULL
UNION ALL
SELECT 1
FROM #Instances i
JOIN M_Instance_Plate m ON i.PlateID = m.Instance_Plate_Plate_ID_LNK
WHERE m.Instance_Plate_Room_Instance_ID_LNK = p.Instance_Plate_Room_Instance_ID_LNK
GROUP BY i.PlateID
HAVING COUNT(*) != 1
)
Try this instead. Is equivalent expression (checks if plateId exists on your table variable and then matches with your variable #numberofplates)
HAVING #NumberofPlates = (
SELECT COUNT(1) AS cc
FROM #Instances AS a
WHERE a.PlateID = Instance_Plate_Plate_ID_LNK
)

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.