Similar questions have already appeard here, but it seems like I'm doing the same as in other instructions, but it doesn't work. So I have
Declare #Counter Int
Set #Counter = 1
while #Counter <= 1000
Begin
insert into Kiso_task_table ([Numbers],[Square_root])
values ( #Counter, Sqrt(#Counter));
Set #Counter = #Counter + 1;
CONTINUE;
End
SELECT TOP (1000) [Numbers],[Square_root]
FROM [Kiso_task].[dbo].[Kiso_task_table]
and it should give me Numbers from 1 to 1000 and their square roots, respectively - instead it produces "1" all the times? Do you know what is wrong?
Your error is in the type of variable to convert the square root, it must be of the 'float' type
CREATE TABLE #Kiso_task_table
(
[Numbers] INT,
[Square_root] FLOAT,
);
GO
DECLARE #Counter INT
SET #Counter = 1
WHILE #Counter <= 1000
BEGIN
INSERT INTO #Kiso_task_table ([Numbers],[Square_root]) VALUES ( #Counter, Sqrt(#Counter));
SET #Counter = #Counter + 1
CONTINUE
END
SELECT TOP (1000) [Numbers],[Square_root]
FROM #Kiso_task_table
SQRT (Transact-SQL)
Your approach is procedural thinking. Try to start thinking set-based. That means: No CURSOR, no WHILE, no loopings, no do this and then this and finally this. Let the engine know, what you want to get back and let the engine decide how to do this.
DECLARE #mockupTarget TABLE(Number INT, Square_root DECIMAL(12,8));
WITH TallyOnTheFly(Number) AS
(
SELECT TOP 1000 ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values
)
INSERT INTO #mockupTarget(Number,Square_root)
SELECT Number,SQRT(Number)
FROM TallyOnTheFly;
SELECT * FROM #mockupTarget ORDER BY Number;
The tally-cte will create a volatile set of 1000 numbers. This is inserted in one single statement.
Btw
I tested your code against my mockup table and it was working just fine...
Related
I have this code:
DECLARE #TotalPayment DECIMAL(18,4)
DECLARE #GetTotalPaymentAmount AS TABLE
(
Amount DECIMAL(18,4),
CurrencyId CHAR(3)
)
INSERT INTO #GetTotalPaymentAmount
SELECT SUM(Amount), CurrencyId
FROM [dbo].[fn_DepositWithdrawReport]()
WHERE OperationTypeId = 2
GROUP BY CurrencyId
SET #TotalPayment = (SELECT Amount FROM #GetTotalPaymentAmount)
I am getting this error
Subquery returned more than 1 value.
So yes, I know that the issue in SET logic because #GetTotalPayment returning more than one row. If I am using for example TOP 1, it is working great, but I need all values of that table. How could I get all values and assign them to local variables from that table?
I am getting table like this
A 'C
---'---
10 'USD
20 'EURO
'
and I need to retrieve all of these values.
Please note that I do not know how many rows will be returned from temp table and saying just declare second variable won't work. The whole point of this would be eventually pass that variables to function as input parameter.
Here, I modified your code slightly. Should work :)
DECLARE #TotalPayment DECIMAL(18,4)
DECLARE #GetTotalPaymentAmount AS TABLE
(
Id int identity(1,1),--added Id column
Amount DECIMAL(18,4),
CurrencyId CHAR(3)
)
INSERT INTO #GetTotalPaymentAmount
SELECT SUM(Amount),CurrencyId
FROM [dbo].[fn_DepositWithdrawReport]()
WHERE OperationTypeId = 2
GROUP BY CurrencyId
declare #i int, #cnt int
set #i = 1
select #cnt = COUNT(*) from #GetTotalPaymentAmount
while #i <= #cnt
begin
select #TotalPayment = Amount from #GetTotalPaymentAmount where Id = #i
--do stuff with retrieved value
#i += 1
end
#So_Op
If you want the data to use in a SCALAR function then just do this
SELECT
G.Amount
,dbo.FN_ScalarFunction(G.Amount)
FROM
#GetTotalPaymentAmount G
If it's a TABLE Function then this works
SELECT
G.Amount
,F.ReturnValue
FROM
#GetTotalPaymentAmount G
CROSS APPLY
dbo.FN_TableFunction(G.Amount) F
Could someone please advise on how to repeat the query if it returned no results. I am trying to generate a random person out of the DB using RAND, but only if that number was not used previously (that info is stored in the column "allready_drawn").
At this point when the query comes over the number that was drawn before, because of the second condition "is null" it does not display a result.
I would need for query to re-run once again until it comes up with a number.
DECLARE #min INTEGER;
DECLARE #max INTEGER;
set #min = (select top 1 id from [dbo].[persons] where sector = 8 order by id ASC);
set #max = (select top 1 id from [dbo].[persons] where sector = 8 order by id DESC);
select
ordial,
name_surname
from [dbo].[persons]
where id = ROUND(((#max - #min) * RAND() + #min), 0) and allready_drawn is NULL
The results (two possible outcomes):
Any suggestion is appreciated and I would like to thank everyone in advance.
Just try this to remove the "id" filter so you only have to run it once
select TOP 1
ordial,
name_surname
from [dbo].[persons]
where allready_drawn is NULL
ORDER BY NEWID()
#gbn that's a correct solution, but it's possible it's too expensive. For very large tables with dense keys, randomly picking a key value between the min and max and re-picking until you find a match is also fair, and cheaper than sorting the whole table.
Also there's a bug in the original post, as the min and max rows will be selected only half as often as the others, as each maps to a smaller interval. To fix generate a random number from #min to #max + 1, and truncate, rather than round. That way you map the interval [N,N+1) to N, ensuring a fair chance for each N.
For this selection method, here's how to repeat until you find a match.
--drop table persons
go
create table persons(id int, ordial int, name_surname varchar(2000), sector int, allready_drawn bit)
insert into persons(id,ordial,name_surname,sector, allready_drawn)
values (1,1,'foo',8,null),(2,2,'foo2',8,null),(100,100,'foo100',8,null)
go
declare #min int = (select top 1 id from [dbo].[persons] where sector = 8 order by id ASC);
declare #max int = 1+ (select top 1 id from [dbo].[persons] where sector = 8 order by id DESC);
set nocount on
declare #results table(ordial int, name_surname varchar(2000))
declare #i int = 0
declare #selected bit = 0
while #selected = 0
begin
set #i += 1
insert into #results(ordial,name_surname)
select
ordial,
name_surname
from [dbo].[persons]
where id = ROUND(((#max - #min) * RAND() + #min), 0, 1) and allready_drawn is NULL
if ##ROWCOUNT > 0
begin
select *, #i tries from #results
set #selected = 1
end
end
I have this query..
Begin
declare #Col int,
#lev int,
#Plan int
select #Col = 411
select #lev = 16
select #Plan = 780
--Insert into baCodeLibrary(Plan_Num,Level_Num,Column_Num,Block_Num,Code_Id,Sort_Order,isactive,Added_By,DateTime_Added,Updated_By,DateTime_Updated)
Select Distinct
#plan,
#lev,
#col,
ba_Object_Id - 5539,
ba_Object_Id,
ba_OBject_Desc,
ba_Object_Id - 5539,
1,
'xyz',
GETDATE(),
'xyz',
GETDATE()
from baObject
where ba_OBject_Id > 5539
and ba_Object_Id < 5554
end
Here I have only for the #col = 411, but I want to loop for all the column until 489
Could any body help me out how to write the loop in this query to select all the columns from 411 to 489?
Thanks in advance
How about not thinking about this in terms of a loop, and instead think about it in terms of a set?
declare #lev int,
#Plan int;
select #lev = 16,
#Plan = 780;
;WITH n(n) AS
(
SELECT TOP (489-411) Number
FROM master.dbo.spt_values
WHERE type = N'P' ORDER BY Number
)
--Insert dbo.baCodeLibrary(...)
SELECT DISTINCT
#plan,
#lev,
n.n,
ba.ba_Object_Id - 5539,
ba.ba_Object_Id,
ba.ba_Object_Desc,
ba.ba_Object_Id - 5539,
1,
'xyz',
GETDATE(),
'xyz',
GETDATE()
FROM dbo.baObject AS ba CROSS JOIN n
where ba.ba_Object_Id > 5539
and ba.ba_Object_Id < 5554;
There's no actual looping mechanism in your query as it exists now. You'll need to implement something like a while loop in order to get that functionality.
See: http://technet.microsoft.com/en-us/library/ms178642.aspx
It'll look something like
DECLARE #counter int
SET #counter = 1
WHILE #counter < 10
BEGIN
-- loop logic
SET #counter = #counter + 1
END
Looping can be performed three ways in T-SQL; a WHILE loop, a SET based operation, or a CURSOR.
T-SQL / SQL Server are optimised for SET based operations, and they are easily the most efficient way to loop, but it all depends on what you're trying to achieve.
You may be warned away from cursors, with good reason, but they are perfectly acceptable as long as you understand what you're doing. Here's an example of a very simple, fast cursor:
DECLARE #myColumn VARCHAR(100)
DECLARE cTmp CURSOR FAST_FORWARD FOR
SELECT MyColumn
FROM MyTable (nolock)
OPEN cTmp
FETCH NEXT FROM cTmp INTO #myColumn
WHILE ##FETCH_STATUS = 0
BEGIN
-- Do something with #myColumn here
FETCH NEXT FROM cTmp INTO #myColumn
END
CLOSE cTmp
DEALLOCATE cTmp
GO
Please do not use this code without reading up on cursors first - they should only be used when SET based operations are not suitable. It depends entirely on what you're trying to achieve - cursors can be very resource hungry, and can result in locked records / tables if you're not careful with the hints you apply to them.
I have the following SQL query:
DECLARE #MyVar datetime = '1/1/2010'
SELECT #MyVar
This naturally returns '1/1/2010'.
What I want to do is have a list of dates, say:
1/1/2010
2/1/2010
3/1/2010
4/1/2010
5/1/2010
Then i want to FOR EACH through the numbers and run the SQL Query.
Something like (pseudocode):
List = 1/1/2010,2/1/2010,3/1/2010,4/1/2010,5/1/2010
For each x in List
do
DECLARE #MyVar datetime = x
SELECT #MyVar
So this would return:-
1/1/2010
2/1/2010
3/1/2010
4/1/2010
5/1/2010
I want this to return the data as one resultset, not multiple resultsets, so I may need to use some kind of union at the end of the query, so each iteration of the loop unions onto the next.
edit
I have a large query that accepts a 'to date' parameter, I need to run it 24 times, each time with a specific to date which I need to be able to supply (these dates are going to be dynamic) I want to avoid repeating my query 24 times with union alls joining them as if I need to come back and add additional columns it would be very time consuming.
SQL is primarily a set-orientated language - it's generally a bad idea to use a loop in it.
In this case, a similar result could be achieved using a recursive CTE:
with cte as
(select 1 i union all
select i+1 i from cte where i < 5)
select dateadd(d, i-1, '2010-01-01') from cte
Here is an option with a table variable:
DECLARE #MyVar TABLE(Val DATETIME)
DECLARE #I INT, #StartDate DATETIME
SET #I = 1
SET #StartDate = '20100101'
WHILE #I <= 5
BEGIN
INSERT INTO #MyVar(Val)
VALUES(#StartDate)
SET #StartDate = DATEADD(DAY,1,#StartDate)
SET #I = #I + 1
END
SELECT *
FROM #MyVar
You can do the same with a temp table:
CREATE TABLE #MyVar(Val DATETIME)
DECLARE #I INT, #StartDate DATETIME
SET #I = 1
SET #StartDate = '20100101'
WHILE #I <= 5
BEGIN
INSERT INTO #MyVar(Val)
VALUES(#StartDate)
SET #StartDate = DATEADD(DAY,1,#StartDate)
SET #I = #I + 1
END
SELECT *
FROM #MyVar
You should tell us what is your main goal, as was said by #JohnFx, this could probably be done another (more efficient) way.
You could use a variable table, like this:
declare #num int
set #num = 1
declare #results table ( val int )
while (#num < 6)
begin
insert into #results ( val ) values ( #num )
set #num = #num + 1
end
select val from #results
This kind of depends on what you want to do with the results. If you're just after the numbers, a set-based option would be a numbers table - which comes in handy for all sorts of things.
For MSSQL 2005+, you can use a recursive CTE to generate a numbers table inline:
;WITH Numbers (N) AS (
SELECT 1 UNION ALL
SELECT 1 + N FROM Numbers WHERE N < 500
)
SELECT N FROM Numbers
OPTION (MAXRECURSION 500)
declare #counter as int
set #counter = 0
declare #date as varchar(50)
set #date = cast(1+#counter as varchar)+'/01/2013'
while(#counter < 12)
begin
select cast(1+#counter as varchar)+'/01/2013' as date
set #counter = #counter + 1
end
Off course an old question. But I have a simple solution where no need of Looping, CTE, Table variables etc.
DECLARE #MyVar datetime = '1/1/2010'
SELECT #MyVar
SELECT DATEADD (DD,NUMBER,#MyVar)
FROM master.dbo.spt_values
WHERE TYPE='P' AND NUMBER BETWEEN 0 AND 4
ORDER BY NUMBER
Note : spt_values is a Mircrosoft's undocumented table. It has numbers for every type. Its not suggestible to use as it can be removed in any new versions of sql server without prior information, since it is undocumented. But we can use it as quick workaround in some scenario's like above.
[CREATE PROCEDURE [rat].[GetYear]
AS
BEGIN
-- variable for storing start date
Declare #StartYear as int
-- Variable for the End date
Declare #EndYear as int
-- Setting the value in strat Date
select #StartYear = Value from rat.Configuration where Name = 'REPORT_START_YEAR';
-- Setting the End date
select #EndYear = Value from rat.Configuration where Name = 'REPORT_END_YEAR';
-- Creating Tem table
with [Years] as
(
--Selecting the Year
select #StartYear [Year]
--doing Union
union all
-- doing the loop in Years table
select Year+1 Year from [Years] where Year < #EndYear
)
--Selecting the Year table
selec]
I have a While loop where I am trying to insert.
DECLARE #CurrentOffer int =121
DECLARE #OldestOffer int = 115
DECLARE #MinClubcardID bigint=0
DECLARE #MaxClubcardID bigint=1000
WHILE 1 = 1
BEGIN
INSERT INTO Temp WITH (TABLOCK)
SELECT top (100) clubcard from TempClub with (nolock) where ID between
#MinClubcardand and #MaxClubcard
declare #sql varchar (8000)
while #OldestOffer <= #CurrentOffer
begin
print #CurrentOffer
print #OldestOffer
set #sql = 'delete from Temp where Clubcard
in (select Clubcard from ClubTransaction_'+convert(varchar,#CurrentOffer)+' with (nolock))'
print (#sql)
exec (#sql)
SET #CurrentOffer = #CurrentOffer-1
IF #OldestOffer = #CurrentOffer
begin
-- my logic
end
end
end
My TempClub table always checks only with first 100 records. My TempClub table has 3000 records.
I need to check all my clubcard all 3000 records with ClubTransaction_121,ClubTransaction_120,ClubTransaction_119 table.
The SELECT query in line 8 returns only the top 100 items
SELECT top (100) clubcard from TempClub ...
If you want to retrieve all items, remove the top (100) part of your statement
SELECT clubcard from TempClub ...
In order to do batch type processing, you need to set the #MinClubcardID to the last ID processed plus 1 and include an ORDER BY ID to ensure that the records are being returned in order.
But... I wouldn't use the approach of using the primary key as my "index". What you're looking for is a basic pagination pattern. In SQL Server 2005+, Microsoft introduced the row_number() function which makes pagination a lot easier.
For example:
DECLARE #T TABLE (clubcard INT)
DECLARE #start INT
SET #start = 0
WHILE(1=1)
BEGIN
INSERT #T (clubcard)
SELECT TOP 100 clubcard FROM
(
SELECT clubcard,
ROW_NUMBER() OVER (ORDER BY ID) AS num
FROM dbo.TempClub
) AS t
WHERE num > #start
IF(##ROWCOUNT = 0) BREAK;
-- update counter
SET #start = #start + 100
-- process records found
-- make sure temp table is empty
DELETE FROM #T
END