cursor for sql server2005 - sql

This is my cursor procedure:
DECLARE C1 CURSOR LOCAL FOR
SELECT DISTINCT
PPTR_MATCH_REF_NO,
PPTR_LDGR_CODE,
PPTR_SLMAST_ACNO,
PPTR_PPN_STATUS
FROM GLAS_PPN_TRANSACTIONS
WHERE PPTR_COMP_CODE = #COMP_CODE
AND ISNULL(PPTR_PPN_STATUS, 'X') = 'V'
DECLARE #MATCH_REF_NO NUMERIC(10,0),
#LDGR_CODE VARCHAR(MAX),
#SLMAST_ACNO VARCHAR(MAX),
#PPN_STATUS VARCHAR(2),
#ACCT_NAME VARCHAR(MAX)
BEGIN
OPEN C1
FETCH NEXT FROM C1 INTO #MATCH_REF_NO,#LDGR_CODE,#SLMAST_ACNO,#PPN_STATUS
WHILE ##FETCH_STATUS =0
BEGIN
-- SET #MATCH_REF_NO = PPTR_MATCH_REF_NO
-- SET #LDGR_CODE = PPTR_LDGR_CODE
-- SET #SLMAST_ACNO = PPTR_SLMAST_ACNO
SELECT #ACCT_NAME = COAD_PTY_FULL_NAME
FROM GLAS_SBLGR_MASTERS,
GLAS_PTY_ADDRESS
WHERE SLMA_COMP_CODE = #COMP_CODE
AND SLMA_LDGRCTL_YEAR = DBO.GLAS_VALIDATIONS_GET_OPEN_YEAR(#COMP_CODE)
AND SLMA_LDGRCTL_CODE = #LDGR_CODE
AND SLMA_STATUS = 'A'
AND SLMA_ACNO = #SLMAST_ACNO
AND COAD_COMP_CODE = SLMA_COMP_CODE
AND COAD_ADDR_ID = SLMA_ADDR_ID
IF #PPN_STATUS = 'V'
BEGIN
SELECT #PPN_STATUS = 'VER'
END
FETCH NEXT FROM C1 INTO #MATCH_REF_NO,#LDGR_CODE,#SLMAST_ACNO,#PPN_STATUS
END
CLOSE C1
END
DEALLOCATE C1
END
How can I retrive #MATCH_REF_NO, #LDGR_CODE, #SLMAST_ACNO, #PPN_STATUS and #ACCT_NAME at the same time?
here 2 select statements is there how can i combine

You can simplify this by using a common-table expression (CTE) in SQL Server 2005 - you'll get something like:
WITH Select1 AS
(
SELECT DISTINCT
PPTR_MATCH_REF_NO,
PPTR_LDGR_CODE,
PPTR_SLMAST_ACNO,
PPTR_PPN_STATUS
FROM GLAS_PPN_TRANSACTIONS
WHERE PPTR_COMP_CODE = #COMP_CODE
AND ISNULL(PPTR_PPN_STATUS, 'X') = 'V'
)
SELECT
COAD_PTY_FULL_NAME
FROM
GLAS_SBLGR_MASTERS, GLAS_PTY_ADDRESS, Select1
WHERE
SLMA_COMP_CODE = Select1.COMP_CODE
AND SLMA_LDGRCTL_YEAR = DBO.GLAS_VALIDATIONS_GET_OPEN_YEAR(Select1.COMP_CODE)
AND SLMA_LDGRCTL_CODE = Select1.LDGR_CODE
AND SLMA_STATUS = 'A'
AND SLMA_ACNO = Select1.SLMAST_ACNO
AND COAD_COMP_CODE = SLMA_COMP_CODE
AND COAD_ADDR_ID = SLMA_ADDR_ID
What I cannot determine from the code you posted is how the GLAS_SBLGR_MASTERS and the GLAS_PTY_ADDRESS are joined (on what condition). Just specifying these two in the FROM clause should be avoided - use the standard ANSI SQL JOIN statements:
FROM GLAS_SBLGR_MASTERS
INNER JOIN GLAS_PTY_ADDRESS ON ???????
Marc

Related

Using different set of WHERE clauses in stored procedure depending on Parameter value

I have 2 stored procedures which return the same columns that I am trying to merge into a single procedure. They both have a different set of parameters and both have different WHERE clauses, but they use the same tables and select the exact same rows.
WHERE clause 1: (uses #UIOID, and #Level)
WHERE ( #UIOID = CASE WHEN #Level = 'Single' THEN C.C_UIOID_PK
WHEN #Level = 'Children' THEN CLC.UIOL_P
WHEN #Level = 'Parent' THEN CLP.UIOL_C
END
OR ( #UIOID = '0'
AND #Level = 'All'
)
)
Where clause 2: (Uses #TeamCode, #Year, #IncludeQCodes)
WHERE C.C_IsChild = 0
AND C.C_MOA <> 'ADD'
AND #TeamCode = C.C_OffOrg
AND C.C_Active = 'Y'
AND ( #Year BETWEEN dbo.f_GetAcYearByDate(C.C_StartDate) AND dbo.f_GetAcYearByDate(C.C_EndDate)
OR #Year = 0 )
AND ( C.C_InstCode NOT LIKE 'Q%'
OR #IncludeQCodes = 1 )
Ideally I want to add a new parameter which basically tells it which of the two WHERE clauses to run, but I can't seem to recreate that with CASE statement because as far as I can tell, they only work for a single WHERE clause, not a whole set of different clauses
I want to do this without having to repeat the select statement again and putting the whole thing in IF statements, and i don't want to put the query into a string either. I just want one select statement ideally.
The problem with using temp tables is the query itself takes a while to run without any parameters and is used in a live website, so I don't want it to have to put all records in a temp table and then filter it.
The problem with using a CTE is you can't follow it with an IF statement, so that wouldn't work either.
Here is the sort of logic I am trying to achieve:
SELECT A
B
C
FROM X
IF #WhichOption = 1 THEN
WHERE ( #UIOID = CASE WHEN #Level = 'Single' THEN C.C_UIOID_PK
WHEN #Level = 'Children' THEN CLC.UIOL_P
WHEN #Level = 'Parent' THEN CLP.UIOL_C
END
OR ( #UIOID = '0'
AND #Level = 'All'
)
)
ELSE IF #WhichOption = 2 THEN
WHERE C.C_IsChild = 0
AND C.C_MOA <> 'ADD'
AND #TeamCode = C.C_OffOrg
AND C.C_Active = 'Y'
AND ( #Year BETWEEN dbo.f_GetAcYearByDate(C.C_StartDate) AND dbo.f_GetAcYearByDate(C.C_EndDate)
OR #Year = 0 )
AND ( C.C_InstCode NOT LIKE 'Q%'
OR #IncludeQCodes = 1 )
Save the following process in a procedure. You can also directly insert into a physical table.
declare #varTable Table (columns exactly as Procedures return)
if(condition is met)
begin
insert into #varTable
exec proc1
end
else
begin
insert into #varTable
exec proc2
end
Add the parameter that you said that it would indicate what filter apply :
select XXXXX
from XXXXX
where (#Mode = 1 and ( filter 1 ))
or
(#Mode = 2 and ( filter 2 ))
option(recompile)
If the #Mode parameter is 1 then it will evaluate the filter 1, otherwise it will evaluate the filter 2.
Add an option(recompile) at the end of the statement, so the SQL engine will replace the variables with their values, eliminate the filter that won't be evaluated, and generate an execution plant for just the filter that you want to apply.
PS: Please notice that although these catchall queries are very easy to code and maintain, and generate a perfectly functional and optimal execution, they are not advised for high-demand applications. The option(recompile) forces the engine to recompile and generate a new execution plan at every execution and that would have a noticeable effect on performance if your query needs to be executed hundreds of times per minute. But for the occasional use it's perfectly fine.
Try to use dynamic SQL:
DECLARE #sql NVARCHAR(max), #where NVARCHAR(max), #WhichOption INT = 1;
SET #sql = 'SELECT A
B
C
FROM X';
IF #WhichOption = 1
SET #where = 'WHERE ( #UIOID = CASE WHEN #Level = ''Single'' THEN C.C_UIOID_PK
WHEN #Level = ''Children'' THEN CLC.UIOL_P
WHEN #Level = ''Parent'' THEN CLP.UIOL_C
END
OR ( #UIOID = ''0''
AND #Level = ''All''
)
)';
ELSE IF #WhichOption = 2
SET #where = ' WHERE C.C_IsChild = 0
AND C.C_MOA <> ''ADD''
AND #TeamCode = C.C_OffOrg
AND C.C_Active = ''Y''
AND ( #Year BETWEEN dbo.f_GetAcYearByDate(C.C_StartDate)
AND dbo.f_GetAcYearByDate(C.C_EndDate)
OR #Year = 0 )
AND ( C.C_InstCode NOT LIKE ''Q%''
OR #IncludeQCodes = 1 ) ';
SET #sql = CONCAT(#sql,' ', #where)
PRINT #sql
EXECUTE sp_executesql #sql

Used Cursor inside cursor for update it is showing o rows effected, logic is working when i tried manually

stored procedure
Used Cursor inside cursor for update it is showing 0 rows effected, logic is working when i tried manually, declaring and closing done properly.
any changes do i need to do
or any alternatives than cursor.
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
ALTER PROCEDURE [dbo].[POS_Discount_Report]
#OutletId INT = NULL,
#FromDate DATE = NULL,
#ToDate DATE = NULL,
#DiscountPercent DECIMAL = NULL
AS
begin
SELECT #CutOffInvoiceAmount = AVG(InvoiceAmount) FROM POS_SalesReceiptMaster WHERE StampDate BETWEEN #FromDate AND #ToDate
DECLARE Receipt_cursor CURSOR FOR
SELECT Id FROM POS_SalesReceiptMaster WHERE StampDate BETWEEN #FromDate AND #ToDate AND InvoiceAmount <= #CutOffInvoiceAmount
OPEN Receipt_cursor
FETCH NEXT FROM Receipt_cursor
INTO #ReceiptId
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE Item_cursor CURSOR FOR
SELECT Id FROM Updated_SalesReceiptItems WHERE ReceiptId = #ReceiptId
OPEN Item_cursor
FETCH NEXT FROM Item_cursor
INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Percentage = Percentage, #ItemPrice = Price FROM
Updated_SalesReceiptItems WHERE Id = #ID
IF #Percentage = 5
BEGIN
SELECT #UpdatePercentage = Tax5 FROM Updated_Master
Where Percentage = #DiscountPercent
END
ELSE
BEGIN
#UpdatePercentage = 5
END
UPDATE Updated_SalesReceiptItems
SET ProductId = Product.ProductId,
Actualprice = Product.Actualprice,
Quantity = Product.Qty,
ProductName = Product.ProductName,
unit = Product.unit,
CategoryName= Product.CategoryName,
Percentage= Product.Percentage,
Amount = Product.Amount FROM
(SELECT TOP 1 PM.ProductId, ProductCode,
dbo.fn_Get_ProductPrice_By_Outlet(ProductId,#OutletId)
AS
Actualprice,
(CASE WHEN ( dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId) != 0)
THEN (#ItemPrice / dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId))
ELSE 0
END) AS Qty,
ProductName, Unit, CategoryName, #UpdatePercentage AS Percentage,
dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId) * (#UpdatePercentage/100) AS TaxAmount
FROM dbo.Products_Master PM
INNER JOIN ProductCategory_Master CM ON PM.CategoryId = CM.CategoryId
INNER JOIN tax_master TM ON PM.TaxId = TM.Id
WHERE (#ItemPrice) % nullif(dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId),0) = 0
AND Percentage = #UpdatePercentage) Product
WHERE Id = #ID
end
FETCH NEXT FROM Item_cursor
INTO #ID
END
CLOSE Item_cursor;
DEALLOCATE Item_cursor;
FETCH NEXT FROM Receipt_cursor
INTO #ReceiptId
END
CLOSE Receipt_cursor;
DEALLOCATE Receipt_cursor;
END
Okay, this is pretty scrappy, and probably won't work without some fixing, but it should give you the general pattern for doing all of this in a single query?
ALTER PROCEDURE POS_Discount_Report (
#OutletId INT = NULL,
#FromDate DATE = NULL,
#ToDate DATE = NULL,
#DiscountPercent DECIMAL = NULL)
AS
BEGIN
DECLARE #CutOffInvoiceAmount NUMERIC(19,2); --?? seems to be missing from original procedure
SELECT #CutOffInvoiceAmount = AVG(InvoiceAmount) FROM POS_SalesReceiptMaster WHERE StampDate BETWEEN #FromDate AND #ToDate; --What happens if one or both of these is NULL?
--CTEs
WITH Receipt AS (
SELECT Id FROM POS_SalesReceiptMaster WHERE StampDate BETWEEN #FromDate AND #ToDate AND InvoiceAmount <= #CutOffInvoiceAmount),
Item AS (
SELECT Id FROM Updated_SalesReceiptItems s INNER JOIN Receipt r ON s.ReceiptId = r.Id),
PercentQuery AS (
SELECT i.Id, u.[Percentage], u.Price FROM Updated_SalesReceiptItems u INNER JOIN Item i ON u.Id = i.Id),
UpdatePercent AS (
SELECT p.Id, p.[Percentage], p.Price, CASE WHEN p.[Percentage] = 5 THEN u.Tax5 ELSE 5 END AS UpdatePercentage FROM PercentQuery p INNER JOIN Updated_Master u ON u.[Percentage] = #DiscountPercent)
UPDATE
u
SET
ProductId = pm.ProductId,
Actualprice = dbo.fn_Get_ProductPrice_By_Outlet(ProductId, #OutletId),
Quantity =
CASE
WHEN (dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId) != 0)
THEN (#ItemPrice / dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId))
ELSE 0
END,
ProductName = pm.ProductName,
unit = pm.unit, --not sure on the alias here, as it's missing in the original query
CategoryName = pm.CategoryName,
[Percentage] = u.UpdatePercentage,
Amount = dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId) * (u.UpdatePercentage / 100) --although this was TaxAmount originally??!
FROM
dbo.Products_Master pm
INNER JOIN ProductCategory_Master cm ON cm.CategoryId = pm.CategoryId
INNER JOIN tax_master tm ON tm.Id = pm.TaxId
INNER JOIN UpdatePercent up ON up.Id = pm.Id
INNER JOIN Updated_SalesReceiptItems u ON u.Id = up.Id
WHERE
(p.Price) % NULLIF(dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, pm.ProductId), 0) = 0
AND [Percentage] = UpdatePercentage;
END;
Basically, I use nested common-table expressions to perform the same action as your original cursors, but these are now set-based. This means I can JOIN the results to the table to be updated and perform all of the updates in a single hit.
I almost certainly got some of this wrong, as I could see a number of parts in your original query that just seemed incorrect?

SELECT case using a variable which can be set based on a parameter

I'd like to select a particular value from a table while using an information from another database that is set based on a current database's value.
So a select case to find the operator code and set the DB path.. then use the same path and collate the result.
DECLARE #DB varchar (1000)
CASE
WHEN #Operator= 1 THEN SET #DB = '{SERVERNAME\ENTITY\DBNAME}'
WHEN #Operator= 2 THEN SET #DB = '{SERVERNAME2\ENTITY2\DBNAME2}'
WHEN #Operator= 3 THEN SET #DB = '{SERVERNAME3\ENTITY3\DBNAME3}'
Select transItem_item collate SQL_Latin1General_CI_AS
FROM Group_Transactions
INNER JOIN #DB.Table_Trans
ON (transItem.item_id collate SQL_Latin1General_CI-AS = Table_Trans.item_id)
Where ---Condition
Control flow method (likely to be the most efficient):
IF #Operator = 1
BEGIN
SELECT stuff
FROM Group_Transactions
INNER
JOIN "Server1\Instance1".Database1.Schema.Table_Trans
ON Group_Transactions... = Table_Trans...
WHERE things...
;
END
ELSE IF #Operator = 2
BEGIN
SELECT stuff
FROM Group_Transactions
INNER
JOIN "Server2\Instance2".Database2.Schema.Table_Trans
ON Group_Transactions... = Table_Trans...
WHERE things...
;
END
ELSE IF #Operator = 3
BEGIN
SELECT stuff
FROM Group_Transactions
INNER
JOIN "Server3\Instance3".Database3.Schema.Table_Trans
ON Group_Transactions... = Table_Trans...
WHERE things...
;
END
;
Single [conditional] query method:
SELECT Group_Transactions.stuff
, trans1.other_thing As other_thing1
, trans2.other_thing As other_thing2
, trans3.other_thing As other_thing3
, Coalesce(trans1.other_thing, trans2.other_thing, trans3.other_thing) As other_thing
FROM Group_Transactions
LEFT
JOIN "Server1\Instance1".Database1.Schema.Table_Trans As trans1
ON trans1... = Group_Transactions...
AND trans1.things...
AND #Operator = 1
LEFT
JOIN "Server2\Instance2".Database2.Schema.Table_Trans As trans2
ON trans2... = Group_Transactions...
AND trans2.things...
AND #Operator = 2
LEFT
JOIN "Server3\Instance3".Database3.Schema.Table_Trans As trans3
ON trans3... = Group_Transactions...
AND trans3.things...
AND #Operator = 3
;
If this is TSQL (I am guessing from your colation names) then you are best trying out OPENQUERY to run your join against another database server. If you are querying a database on the same server you could build your query up as a parameter and then run it using EXEC.
Gvee's Control Flow method may be a verbose, but it would work. You might want to create a look up table like my #tbl_Databases if you have a bunch of databases. Here's a dynamic SQL solution:
DECLARE #Operator INT = 1,
#DB VARCHAR(1000);
DECLARE #tbl_Databases TABLE (ID INT IDENTITY(1,1),DB VARCHAR(1000))
INSERT INTO #tbl_Databases(DB)
VALUES ('{SERVERNAME\ENTITY\DBNAME}'),('{SERVERNAME2\ENTITY2\DBNAME2}'),('{SERVERNAME3\ENTITY3\DBNAME3}');
SELECT #DB = DB
FROM #tbl_Databases
WHERE ID = #Operator
SELECT #DB
SELECT
(
'SELECT transItem_item COLLATE SQL_Latin1General_CI_AS
FROM Group_Transactions
INNER JOIN ' + #DB + '.dbo.Table_Trans
ON (transItem.item_id collate SQL_Latin1General_CI-AS = Table_Trans.item_id)
Where 1 = 1'
)

MSSQL merge with distributed transaction alternative

I have a distributed transaction where I need to merge into the target remote table.
Now MERGE INTO isn't allowed according to MSDN: “target_table cannot be a remote table”.
So my workaround goes as follows: 0. begin distributed transaction 1. define a cursor 2. open it 3. if cursor has at least one record (CURSOR_STATUS()=1) fetch next 4. if exists (select top 1 * from target_remote_table where id = #myCurrentCursorId) -> when true update target_remote_table when false insert into target_remote_table 5. commit/rollback distributed transaction depending on trancount and xact_state
It works but I know that cursors are evil and you shouldn't use them. So I want to ask if there is any other way I could solve this by not using cursors?
USE [My_DB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[my_proc_merge_into_remote_table]
#ID_A INT,
#ID_B INT
AS
BEGIN
SET NOCOUNT ON;
-- CURSOR VALUES
DECLARE #field_A INT
DECLARE #field_B INT
DECLARE #field_C INT
DECLARE #field_D BIT
DECLARE #field_E INT
DECLARE #field_F DATETIME
DECLARE #field_G VARCHAR(20)
DECLARE #field_H DATETIME
DECLARE #field_I VARCHAR(20)
BEGIN TRY
BEGIN DISTRIBUTED TRANSACTION
-- CURSOR !!
DECLARE my_cursor CURSOR FOR
SELECT b.field_A ,
b.field_B,
c.field_C,
a.field_D,
a.field_E,
GETDATE() AS field_F,
a.field_G,
GETDATE() AS field_H,
a.field_I
FROM dbo.source_tbl a
LEFT JOIN dbo.base_element_tbl l
ON a.obj_id = l.obj_id AND a.element_id = l.element_id
INNER JOIN dbo.base_obj_tbl b
ON a.obj_id = b.obj_id
INNER JOIN dbo.element_tbl c
ON a.element_id = c.element_id
WHERE a.ID_B = #ID_B
AND a.ID_A = #ID_A;
OPEN my_cursor;
-- check if cursor result set has at least one row
IF CURSOR_STATUS('global', 'my_cursor') = 1 BEGIN
FETCH NEXT FROM my_cursor
INTO #field_A,
#field_B,
#field_C,
#field_D,
#field_E,
#field_F,
#field_G,
#field_H,
#field_I;
WHILE ##FETCH_STATUS = 0 BEGIN
-- HINT: MY_REMOTE_TARGET_TABLE is a Synonym which already points to the correct database and table
IF EXISTS(SELECT TOP 1 * FROM MY_REMOTE_TARGET_TABLE WHERE field_A = #field_A AND field_B = #field_B AND field_C = #field_C AND field_E = #field_E)
UPDATE MY_REMOTE_TARGET_TABLE SET field_D = #field_D, field_H = #field_H, field_I = #field_I;
ELSE
INSERT INTO MY_REMOTE_TARGET_TABLE (field_A, field_B, field_C, field_D, field_E, field_F, field_G, field_H, field_I) VALUES (#field_A, #field_B, #field_C, #field_D, #field_E, #field_F, #field_G, #field_H, #field_I);
FETCH NEXT FROM my_cursor
INTO #field_A,
#field_B,
#field_C,
#field_D,
#field_E,
#field_F,
#field_G,
#field_H,
#field_I;
END;
END;
CLOSE my_cursor;
DEALLOCATE my_cursor;
IF (##TRANCOUNT > 0 AND XACT_STATE() = 1)
BEGIN
COMMIT TRANSACTION
END
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0 AND XACT_STATE() = -1)
ROLLBACK TRANSACTION
END CATCH;
END
Here you go, first inner join target and source to update, then left join to insert missing.
;WITH CTE_Source AS
(
SELECT b.field_A ,
b.field_B,
c.field_C,
a.field_D,
a.field_E,
GETDATE() AS field_F,
a.field_G,
GETDATE() AS field_H,
a.field_I
FROM dbo.source_tbl a
LEFT JOIN dbo.base_element_tbl l
ON a.obj_id = l.obj_id AND a.element_id = l.element_id
INNER JOIN dbo.base_obj_tbl b
ON a.obj_id = b.obj_id
INNER JOIN dbo.element_tbl c
ON a.element_id = c.element_id
WHERE a.ID_B = #ID_B
AND a.ID_A = #ID_A;
)
UPDATE trgt
SET trgt.field_D = src.field_D, trgt.field_H = src.field_H, trgt.field_I = src.field_I
FROM MY_REMOTE_TARGET_TABLE trgt
INNER JOIN CTE_Source src ON src.field_A = trgt.field_A AND src.field_B = trgt.field_B AND src.field_C = trgt.field_C AND src.field_E = trgt.field_E
;WITH CTE_Source AS
(
SELECT b.field_A ,
b.field_B,
c.field_C,
a.field_D,
a.field_E,
GETDATE() AS field_F,
a.field_G,
GETDATE() AS field_H,
a.field_I
FROM dbo.source_tbl a
LEFT JOIN dbo.base_element_tbl l
ON a.obj_id = l.obj_id AND a.element_id = l.element_id
INNER JOIN dbo.base_obj_tbl b
ON a.obj_id = b.obj_id
INNER JOIN dbo.element_tbl c
ON a.element_id = c.element_id
WHERE a.ID_B = #ID_B
AND a.ID_A = #ID_A;
)
INSERT INTO MY_REMOTE_TARGET_TABLE (field_A, field_B, field_C, field_D, field_E, field_F, field_G, field_H, field_I)
SELECT field_A, field_B, field_C, field_D, field_E, field_F, field_G, field_H, field_I
FROM CTE_Soure src
LEFT JOIN MY_REMOTE_TARGET_TABLE trgt ON src.field_A = trgt.field_A AND src.field_B = trgt.field_B AND src.field_C = trgt.field_C AND src.field_E = trgt.field_E
WHERE trgt.field_A IS NULL

SQL Server 2000 : generating and incrementing data from column conditionally without using CURSOR

:)
Is there any way to create an index, and incrementing with a given condition, but without CURSOR handling/usage
For example:
The condition in my case is that: "if the current color (this is the item to be checked) is the same as the last one: not increment, otherwise increment in one unit"
This must be in a SQL query with no CURSOR USAGE and of course a good time (work with ... 10000 rows at least)
Thanks in advance.
EDIT: I forgot to mention that NEW_INDEX Column doesn't exist. It must be generated with the with the query.
EDIT2: Is there a way that only make use of SELECT/INSERT/UPDATE statements? (not set, declare...)
Assume a table called Colors with fields ID, Color, and ColorIndex of types int, varchar, and int respectively. I also assume the OP means prev / after based on an ordering of the ID field in asc order.
You could do this without a cursor, and use a while loop...but it definately isn't set based:
DECLARE #MyID int
DECLARE #CurrentIndex int
DECLARE #CurrentColor varchar(50)
DECLARE #PreviousColor varchar(50)
SET #CurrentIndex = (SELECT 0)
SET #MyID = (SELECT TOP 1 ID FROM Colors ORDER BY ID ASC)
SET #CurrentColor = (SELECT '')
SET #PreviousColor = (SELECT Color FROM Colors WHERE ID = #MyID)
WHILE (#MyID IS NOT NULL)
BEGIN
IF (#CurrentColor <> #PreviousColor)
BEGIN
SET #PreviousColor = (SELECT Color FROM Colors WHERE ID = #MyID)
SET #CurrentIndex = (SELECT #CurrentIndex + 1)
UPDATE Colors SET ColorIndex = #CurrentIndex WHERE ID = #MyID
END
ELSE
BEGIN
UPDATE Colors SET ColorIndex = #CurrentIndex WHERE ID = #MyID
SET #PreviousColor = (SELECT Color FROM Colors WHERE ID = #MyID)
END
SET #MyID = (SELECT TOP 1 ID FROM Colors WHERE ID > #MyID ORDER BY ID ASC)
SET #CurrentColor = (SELECT Color FROM Colors WHERE ID = #MyID)
END
The result after execution:
Performance wasn't too shabby as long as ID and color are indexed. The plus side is it is a bit faster then using a regular old CURSOR and it's not as evil. Solution supports SQL 2000, 2005, and 2008 (being that you are using SQL 2000 which did not support CTEs).
declare #ID int,
#MaxID int,
#NewIndex int,
#PrevCol varchar(50)
select #ID = min(ID),
#MaxID = max(ID),
#PrevCol = '',
#NewIndex = 0
from YourTable
while #ID <= #MaxID
begin
select #NewIndex = case when Colour = #PrevCol
then #NewIndex
else #NewIndex + 1
end,
#PrevCol = Colour
from YourTable
where ID = #ID
update YourTable
set NewIndex = #NewIndex
where ID = #ID
set #ID = #ID + 1
end
https://data.stackexchange.com/stackoverflow/q/122958/
select
IDENTITY(int,1,1) as COUNTER
,c1.ID
into
#temp
from
CUSTOMERS c1
left outer join (
select
c1.ID, max(p.ID) as PRV_ID
from
CUSTOMERS c1,
(
select
ID
from
CUSTOMERS
) p
where
c1.ID > p.ID
group by
c1.ID
) k on k.ID = c1.ID
left outer join CUSTOMERS p on p.ID = k.PRV_ID
where
((c1.FAVOURITE_COLOUR < p.FAVOURITE_COLOUR)
or
(c1.FAVOURITE_COLOUR > p.FAVOURITE_COLOUR)
or
p.FAVOURITE_COLOUR is null)
update
CUSTOMERS
set
NEW_INDEX = i.COUNTER
--select *
from
CUSTOMERS
inner join (
select
c1.ID, max(t.COUNTER) as COUNTER
from
CUSTOMERS c1,
(
select
ID
,COUNTER
from
#temp
) t
where
c1.ID >= t.ID
group by
c1.ID
) i on i.ID = CUSTOMERS.ID
drop table #temp
select * from CUSTOMERS