SQL table variable empty after while loop - sql-server

I have a SQL table I'm trying to query unique results. Based off of the "FileName" column I want to get only the most recent row for each filename.
In the example, I am pulling all files with the last name of "smith". The LoanNumber may be in multiple rows because the file may have been copied and so I want the most recent one only.
The code below results in no data. I get just a column header called "FileID" and no values. I believe the #ResultsTable is not keeping the data I'm trying to put into it with the INSERT on the 12th line. I do not know why. I tried moving the DECLARE statement for the table variable #ResultsTable around and the best I can ever get it to display is a single record and most of places I put it, I only get "Must declare the table variable "#ResultsTable"."
What am I doing wrong that the table variable is not getting populated properly and maintaining it rows?
DECLARE #ResultsTable table(FileID varchar(10)); --table variable for the list of record IDs
DECLARE #ThisLoanNumber varchar(50); --current loan number for each record to be used during while loop
DECLARE LoanFiles CURSOR read_only FOR --create LoanFiles cursor to loop through
Select distinct [LoanNumber] from [Package_Files]
Where [LastName]='smith';
OPEN LoanFiles;
While ##FETCH_STATUS = 0 --If previous fetch was successful, loop through the cursor "LoanFiles"
BEGIN
FETCH NEXT FROM LoanFiles into #ThisLoanNumber; --Get the LoanNumber from the current row
INSERT Into #ResultsTable --insert the ID number of the row which was updated most recently of the rows which have a loan number equal to the number form the current row in "LoanFiles" cursor
Select Top 1 [iFileID] From [Package_Files] Where [LoanNumber]=#ThisLoanNumber Order By [UpdateTime] Desc;
END;
CLOSE LoanFiles;
DEALLOCATE LoanFiles;
Select * from #ResultsTable; --display results...

There are a couple of ways you can do this type of query without resorting to looping.
Here is using a cte.
with SortedValues as
(
select FileID
, ROW_NUMBER() over (partition by LoanNumber order by UpdateTime desc) as RowNum
from Package_Files
where LastName = 'Smith'
)
select FileID
from SortedValues
where RowNum = 1
Another option is to use a subquery approach similar to this.
select FileID
from
(
select FileID
, ROW_NUMBER() over (partition by LoanNumber order by UpdateTime desc) as RowNum
from Package_Files
where LastName = 'Smith'
) x
where x.RowNum = 1

Related

Select latest row on duplicate values while transfering table?

I have a logging table that is live which saves my value to a table frequently.
My plan is to take those values and put them on a temporary table with
SELECT * INTO #temp from Block
From there I guess my block table is empty and the logger can keep on logging new values.
The next step is that I want to save them in a existing table. I wanted to use
INSERT INTO TABLENAME(COLUMN1,COLUMN2...) SELECT (COLUMN1,COLUMN2...) FROM #temp
The problem is that the #temp table has duplicates primary keys. And I only want to store the last ID.
I have tried DISTINCT but it didn't work. Could not get ROW_Count to work. Are there any ideas on how I should do it? I wish to make it with as few reads as possible.
Also, in the future I plan to send them to another database, how do I do that on SQL Server? I guess it's something like FROM Table [in databes]?
I couldn't get the blocks to copy. But here goes:
create TABLE Product_log (
Grade char(64),
block_ID char(64) PRIMARY KEY NOT NULL,
Density char(64),
BatchNumber char(64) NOT NULL,
BlockDateID Datetime
);
That is my table i want to store the data in. There I do not wish to have duplicates on the id. The problem is, while logging I get duplicates since I log on change. Lets say that the batchid is 1, if it becomes 2 while logging. I will get a blockid twice, both with batch number 1 and 2. How do I pick the latter?
Hope I explained enough for guidance. While logging they look like this:
id SiemensTiaV15_s71200_BatchTester_NewBatchIDValue_VALUE SiemensTiaV15_s71200_BatchTester_TestWriteValue_VALUE SiemensTiaV15_s71200_BatchTester_TestWriteValue_TIMESTAMP SiemensTiaV15_s71200_MainTank_Density_VALUE SiemensTiaV15_s71200_MainTank_Grade_VALUE
1 00545 S0047782 2020-06-09 11:18:44.583 0 xxxxx
2 00545 S0047783 2020-06-09 11:18:45.800 0 xxxxx
Please use below query,
select * from
(select id, SiemensTiaV15_s71200_BatchTester_NewBatchIDValue_VALUE,SiemensTiaV15_s71200_BatchTester_TestWriteValue_VALUE, SiemensTiaV15_s71200_BatchTester_TestWriteValue_TIMESTAMP, SiemensTiaV15_s71200_MainTank_Density_VALUE,SiemensTiaV15_s71200_MainTank_Grade_VALUE,
row_number() over (partition by SiemensTiaV15_s71200_BatchTester_NewBatchIDValue_VALUE order by SiemensTiaV15_s71200_BatchTester_TestWriteValue_TIMESTAMP desc) as rnk
from table_name) qry
where rnk=1;
INTO #temp FROM Block; INSERT INTO Product_log(Grade, block_ID, Density, BatchNumber, BlockDateID)
selct NewBatchIDValue_VALUE, TestWriteValue_VALUE, TestWriteValue_TIMESTAMP,
Density_VALUE, Grade_VALUE from
(select NewBatchIDValue_VALUE, TestWriteValue_VALUE,
TestWriteValue_TIMESTAMP, Density_VALUE, Grade_VALUE, row_number() over
(partition by BatchTester_NewBatchIDValue_VALUE order by
BatchTester_TestWriteValue_VALUE) as rnk from #temp) qry
where rnk = 1;

Execute select query in where condition

I want to execute a select query in where condition for update table to fill number increment as per user account in SQL Server.
Here is my code:
DECLARE #i INT = 0;
WHILE #i <= 1077
begin
update tbl_UdharKhata set ReceiptNo = #i
where EXISTS (select distinct UserId from tbl_udharkhata)
SET #i = #i + 1;
end
this query is working perfectly but the problem is that in ReceiptNo whole user account receipt number updating the same number.
Note: there are 1077 rows of distinct user accounts and hence there is 1077 row of userid.
What you're doing is updating all the records in the table 1077 times, each time with a different number, up to the end of the loop. Now I'm not sure if you want a single number for each userId, or an incrementing number for each row with the same userId, starting at 1 for each userId.
The only way for your SELECT statement to not return anything is if the table is empty - because it has no WHERE clause.
Since your select statement is in an EXISTS operator, the condition will always evaluate to true, making the WHERE clause of the UPDATE statement redundant.
basically, it's like update tbl_UdharKhata set ReceiptNo = #i where exists(select 1),
which is exactly like update tbl_UdharKhata set ReceiptNo = #i
This means that in each iteration of the loop, you're updating all the records in the table with the current value of #i.
Now, it's not very clear from your question what you want, but I'm gonna go on a limb here and guess you want to update the ReceiptNo column so that for each userId you'll have an incrementing number, resetting to 1 for each new userId.
If that is the case, the easiest way to do that is by creating a common table expression (cte) and then update that cte:
;WITH cte AS
(
SELECT ReceiptNo
-- Note: Order by ##SPID means an arbitrary order! Details after the code.
, ROW_NUMBER() OVER(PARTITION BY UserId ORDER BY ##SPID) As Rn
FROM tbl_UdharKhata
)
UPDATE cte
SET ReceiptNo = Rn
Note I've used ORDER BY ##SPID in the OVER clause of the ROW_NUMBER().
##SPID is a built in function that returns the session ID of the current user process - meaning that it will return a constant value in each session.
Using Order by with a constant value will generate an arbitrary order.
If you want a specific order, use any column in your table that isn't userId (otherwise you'll end up with the same arbitrary order - because the userId will be the same for each partition).

Windowed functions can only appear in the SELECT or ORDER BY clauses-update in cursor

Declare #Customerid int
DECLARE ChangeCustomerName CURSOR FOR
select customerid from customer
OPEN ChangeCustomerName
FETCH NEXT FROM ChangeCustomerName into #Customerid
WHILE ##fetch_status = 0
BEGIN
update customer set customername ='Customer'
+convert (varchar(10),ROW_NUMBER() OVER(ORDER BY customerid ASC))
where customerid=#Customerid
FETCH NEXT FROM ChangeCustomerName into #Customerid
END
close ChangeCustomerName
deallocate ChangeCustomerName
Windowed functions can only appear in the SELECT or ORDER BY clauses-update in cursor
You seem to be trying to set customer names to sequential values. For this purpose, you don't need a cursor! Just something like this:
with toupdate as (
select c.*, row_number() over (order by customerid) as seqnum
from customer c
)
update toupdate
set customername = 'Customer' + convert(varchar(10), seqnum);
You should avoid cursors whenever you can. Set-based operations are more efficient and often result in simpler code.
Not sure why you are creating customer names.
Your current approach may work for existing records, but for new entries you have run the update query again and there are possibilities that the same customer gets different customer name after some deletes. So I suggest you to create a computed column.
You can make the customername as computed column
Alter table customer
add customername as 'Customer'+convert (varchar(10),customerid) PERSISTED
Note: It might not be sequential still it will be unique if customerid is unique

SQL get different random data every call

need help for this, can SQL do this?
I have a table with 100 rows, I need to call 10 random rows from the table.
Can SQL make every query call give me different random data but not the chosen rows before
1st random query = get 10 random rows
2nd random query = get another 10 random rows different from the 1st call
.
.
.
10th random query = get the last 10 rows not chosen
I am still searching but no idea how, last option is every select do updates to the rows so not selected in next call
SELECT TOP 10 [your select list]
FROM [your table]
ORDER BY NEWID()
Should give you 10 "random" rows per call.
If you need the rows to be 100% sure they're not selected previously - the only way to do it is to mark them so you can exclude them in where or remove them from table.
edit to expand on the mark approach:
Pseudo code for marking as extracted could look something like this:
DECLARE #tempList AS TABLE
INSERT INTO #tempList
SELECT TOP 10 [your select list]
FROM [your table]
WHERE extracted = 0
ORDER BY NEWID()
UPDATE [your table]
SET extracted = 1
WHERE rows IN #tempList
SELECT * FROM #templist
With this script, you just need to declare which run you are doing, it will always give the same rows for each run, you just have to change the value for #run. Result will seem random without actually being random:
DECLARE #run INT = 2
DECLARE #t table(id int)
INSERT #t
SELECT x.id*10 + y.id + 1
FROM (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x(id)
CROSS JOIN
(values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) y(id)
;WITH CTE as
(
SELECT id, ntile(10) over (order by reverse(CHECKSUM(id, 'abc'))) rowgroup FROM #t
)
SELECT id
FROM CTE
WHERE rowgroup = #run

better than a cursor tsql

The question here is simple (although I am prepared for the answer not to be), how can I make this query more efficient.
In a nutshell it copies records. It selects X records, then using those records data duplicates them capturing the new identifier. Using the id of the original record and new record, it then inserts by copying data of the original data for another table using the new identifier.
This takes a long time. Can you help shorten it?
DECLARE DaysToDuplicateCursor CURSOR FAST_FORWARD FOR
SELECT
DayId
FROM [Days]
WHERE AgentId IN ('XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX')
AND PersonAgentId IN (
'YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY'
,'WWWWWWWW-WWWW-WWWW-WWWW-WWWWWWWWWWWW'
,'ZZZZZZZZ-ZZZZ-ZZZZ-ZZZZ-ZZZZZZZZZZZZ'
,'TTTTTTTT-TTTT-TTTT-TTTT-TTTTTTTTTTTT'
)
DECLARE #Id INT
OPEN DaysToDuplicateCursor
FETCH NEXT FROM DaysToDuplicateCursor INTO #Id
WHILE ##FETCH_STATUS = 0
BEGIN
--
-- Insert Days data.
--
INSERT INTO [Days] (
[DayTemplateId]
,[DayDate]
)
SELECT [DayTemplateId]
,DATEADD(YEAR,-1,[DayDate]) AS [DayDate]
FROM [Days] WHERE [DayId] = #Id
--
-- Insert Periods data.
--
INSERT INTO [Periods] (
[DayId]
,[PeriodTemplateId]
)
SELECT
SCOPE_IDENTITY()
,[PeriodTemplateId]
FROM [Periods] WHERE [DayId] = #Id
--
END
CLOSE DaysToDuplicateCursor
DEALLOCATE DaysToDuplicateCursor
You do not need to use a cursor at all if you use the OUTPUT clause instead of asking for scope_identity. You will put this information into a table varaiable. You will also want to return any other columns in the output clause that uniquely identify the record so you can use them in joins to get the data you need in subsequent inserts.

Resources