How to change the series of zeroes in a computed column - sql-server

I am using the below T-SQL to create a table with a computed column which gives me IDs in 'BID(The Year)-0000'. The issue is that I would like to reset the series of zeros in the ID when the year is changed.For example, the last ID in the table is BID2017-0923 when the year is changed I want the series to be reset for example 'BID2018-0001'.
Here is the T-SQL which currently I am using.
CREATE TABLE Books
(
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
IDCreateDate DATETIME NOT NULL DEFAULT GETDATE(),
BookID AS ('BID' + LTRIM(YEAR(IDCreateDate)) + '-' + RIGHT('0000' + LTRIM(ID), 4)),
ISBN VARCHAR(32),
BookName NVARCHAR(50),
AuthorName NVARCHAR(50),
BLanguage VARCHAR(50),
StaId int,
StuId int,
CatNo int
);
UPDATE:
Furthermore, I would like the ID col to remembers its last id based on the year it has.For example, the last id in the column is BID2017-0920 when I change the year to 2010, it has to reset the number series for example BID2010-0001 but when I switch back the year to 2017, I don't want the series to be reset rather I want it to start from BID2017-0921.

Edit#1: I've updated my solution according to latest OP's comments
/*
CREATE TABLE dbo.CustomSequence (
Name VARCHAR(50) NOT NULL,
Prefix VARCHAR(10) NOT NULL,
LastValue VARCHAR(50) NULL, -- All generated values will have following pattern: PrefixYYYY-SeqvNumber
LastYear SMALLINT NOT NULL,
CONSTRAINT PK_CustomSequence_Name_LastYear PRIMARY KEY (Name, LastYear),
LastNumber SMALLINT NULL
);
GO
-- Config OrderSequence 2014
INSERT dbo.CustomSequence (Name, Prefix, LastValue, LastYear, LastNumber)
VALUES ('BookSequence', 'BID', NULL, 2014, 1)
GO
-- Config OrderSequence 2017
INSERT dbo.CustomSequence (Name, Prefix, LastValue, LastYear, LastNumber)
VALUES ('BookSequence', 'BID', NULL, 2017, 1)
GO
*/
-- Generating new seq
-- IN Parameters
DECLARE #CustomSequenceName VARCHAR(50) = 'BookSequence'
DECLARE #Year SMALLINT = 2017 --YEAR(GETDATE())
DECLARE #LenOfNumber TINYINT = 4
-- End of IN Parameters
-- OUT Parameters
DECLARE #GeneratedValue VARCHAR(50)
-- End of OUT Parameters
UPDATE s
SET LastNumber = IIF(LastValue IS NULL, LastNumber, LastNumber + 1) + IIF(LastNumber = REPLICATE('9', #LenOfNumber), 1/0, 0),
#GeneratedValue = LastValue = Prefix + LTRIM(#Year) + '-' + RIGHT(REPLICATE('0', #LenOfNumber) + LTRIM(IIF(LastValue IS NULL, LastNumber, LastNumber + 1)), #LenOfNumber)
FROM dbo.CustomSequence s
WHERE s.Name = #CustomSequenceName
AND s.LastYear = #Year
-- End of Generating new seq
SELECT #GeneratedValue
SELECT * FROM dbo.CustomSequence
--> BID2017-0001, BID2017-0002, BID2017-0003, ...
Note#1: This solution work with pessimistic concurrency control (default behavior of SQL Server Database Engine)
Note#2: If we are reaching 10000 following piece of code
... IIF(LastNumber = 9999, 1/0, 0) ...
will raise an exception
Msg 8134, Level 16, State 1, Line 30
Divide by zero error encountered.
The statement has been terminated.
Note#3: UPDATE statement could be encapsulated into a stored procedure with #CustomSequenceName VARCHAR(50) as input parameter and #GeneratedValue VARCHAR(50) OUTPUT as OUT[PUT] parameter.
Note#4: #LenOfNumber allows to customize length of sequential number (default is 4)
Edit#2:
UPDATE statement could be replaced with following combination of INSERT + UPDATE statements:
SET XACT_ABORT ON
BEGIN TRAN
IF NOT EXISTS(SELECT * FROM dbo.CustomSequence s WITH(HOLDLOCK) WHERE s.Name = #CustomSequenceName AND s.LastYear = #Year)
BEGIN
INSERT dbo.CustomSequence (Name, Prefix, LastValue, LastYear, LastNumber)
VALUES (#CustomSequenceName, #Prefix, NULL, #Year, 1)
END
UPDATE s
SET LastNumber = IIF(LastValue IS NULL, LastNumber, LastNumber + 1) + IIF(LastNumber = REPLICATE('9', #LenOfNumber), 1/0, 0),
#GeneratedValue = LastValue = Prefix + LTRIM(#Year) + '-' + RIGHT(REPLICATE('0', #LenOfNumber) + LTRIM(IIF(LastValue IS NULL, LastNumber, LastNumber + 1)), #LenOfNumber)
FROM dbo.CustomSequence s WITH(HOLDLOCK)
WHERE s.Name = #CustomSequenceName
AND s.LastYear = #Year
COMMIT

Related

Dynamic columns depend on previous dynamic columns - TSQL

I try to create dynamic forecast for 18(!) months depend on previous columns (months) and i am stuck:
I have three columns:
Stock
SafetyStock
Need for production - another select with clause WHERE date = getdate()
what i need to achieve:
Index, Stock- Current month, SafetyStock-Current month, Need for production (select * from Nfp where date = getdate()), Stock - Current month + 1, Safetystock - Current Month + 1, Need for Production - Current Month + 1 ... etc till 18 months
calculations:
Stock - Current month + 1 = Stock previous month + SafetyStock previous month - Needs for production of current month
there is any possibility to create something like this ? it has to be dynamic and get calculation for current date and next 18 months. So now i have to calculate from 2020-10 till let's say 2022-04
What i have tried:
I prepared 18 cte and joins everything. Then i do calculations - it works but it slow and i think it is not profesional.
I have tried to do dynamic sql, below you can see my code but i have stucked when i wanted to do computed column depended on previous computed column:
------------------- CODE -------------------------
if object_id('tempdb..#tmp') is not null
drop table #tmp
if object_id('tempdb..#tmp2') is not null
drop table #tmp2
declare #cols as int
declare #iteration as int
declare #Mth as nvarchar(30)
declare #data as date
declare #sql as nvarchar(max)
declare #sql2 as nvarchar(max)
set #cols = 18
set #iteration = 0
set #Mth = month(getdate())
set #data = cast(getdate() as date)
select
10 as SS,
12 as Stock
into #tmp
WHILE #iteration < #cols
begin
set #iteration = #iteration + 1
set #sql =
'
alter table #tmp
add [StockUwzgledniajacSS - ' + cast(concat(year(DATEADD(Month, #Iteration, #data)),'-', month(DATEADD(Month, #Iteration, #data))) as nvarchar(max)) +'] as (Stock - SS)
'
exec (#sql)
set #Mth= #Mth+ 1
set #sql2 =
'
alter table #tmp
add [StockUwzgledniajacSS - ' + #Mth +'] as ([StockUwzgledniajacSS - ' + #Mth +'])
'
end
select * from #tmp
thanks in advance!
Update 1 note: I wrote this before you posted your data. This still holds I believe but, of course, stock levels are way different. Given that your NFP data is by day, and your report is by month, I suggest adding something to preprocess that data into months e.g., sum of NPS values, grouped by month.
Update 2 (next day) note: From the OPs comments below, I've tried to integrate this with what was written and more directly answering the question e.g., creating a reporting table #tmp.
Given that the OP also mentions millions of rows, I imagine each row represents a specific part/item - I've included this as a field called StockNum.
I have done something that probably doesn't do your calculations properly, but demonstrates the approach and should get you over your current hurdle. Indeed, if you haven't used these before, then updating this code with your own calculations will help you to understand how it works so you can maintain it.
I'm assuming the key issue here for calculation is that this month's stock is based on last month's stock and then new stock minus old stock for this month.
It is possible to calculate this in 18 separate statements (update table set col2 = some function of col1, then update table set col3 = some function of col2, etc). However, updating the same table multiple times is often an anti-pattern causing poor performance - especially if you need to read the base data again and again.
Instead, something like this is often best calculated using a Recusive CTE (here's an example description), where it 'builds' a set of data based on previous results.
The key difference in this approach is that it
Creates the reporting table (without any data/calculations going in)
Calculates the data as a separate step - but with columns/fields that can be used to link to the reporting table
Inserts the data from calculations into the reporting table as a single insert statement.
I have used temporary tables/etc liberally, to help demonstrate the process.
You haven't explained what safety stock is, nor how you measure what's coming in, so for the example below, I have assumed safety stock is the amount produced and is 5 per month. I've then assumed that NFP is amount going out each month (e.g., forward estimates of sales). The key result will be stock at the end of month (e.g., which you could then review whether it's too high or too low).
As you want to store it in a table that has each month as columns, the first step is to create a list with the relevant buckets (months). These include fields used for matching in later calculations/etc. Note I have included some date fields (startdate and enddate) which may be useful when you customise the code. This part of the SQL is designed to be as straightforward as possible.
We then create the scratch table that has our reference data for stock movements, replacing your SELECT * FROM NFP WHERE date = getdate()
/* SET UP BUCKET LIST TO HELP CALCULATION */
CREATE TABLE #RepBuckets (BucketNum int, BucketName nvarchar(30), BucketStartDate datetime, BucketEndDate datetime)
INSERT INTO #RepBuckets (BucketNum) VALUES
(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),
(11),(12),(13),(14),(15),(16),(17),(18)
DECLARE #CurrentBucketStart date
SET #CurrentBucketStart = DATEFROMPARTS(YEAR(getdate()), MONTH(getdate()), 1)
UPDATE #RepBuckets
SET BucketName = 'StockAtEnd_' + FORMAT(DATEADD(month, BucketNum, #CurrentBucketStart), 'MMM_yy'),
BucketStartDate = DATEADD(month, BucketNum, #CurrentBucketStart),
BucketEndDate = DATEADD(month, BucketNum + 1, #CurrentBucketStart)
/* CREATE BASE DATA */
-- Current stock
CREATE TABLE #Stock (StockNum int, MonthNum int, StockAtStart int, SafetyStock int, NFP int, StockAtEnd int, PRIMARY KEY(StockNum, MonthNum))
INSERT INTO #Stock (StockNum, MonthNum, StockAtStart, SafetyStock, NFP, StockAtEnd) VALUES
(12422, 0, NULL, NULL, NULL, 10)
-- Simulates SELECT * FROM NFP WHERE date = getdate()
CREATE TABLE #NFP_by_month (StockNum int, MonthNum int, StockNFP int, PRIMARY KEY(StockNum, MonthNum))
INSERT INTO #NFP_by_month (StockNum, MonthNum, StockNFP) VALUES
(12422, 1, 4), (12422, 7, 4), (12422, 13, 4),
(12422, 2, 5), (12422, 8, 5), (12422, 14, 5),
(12422, 3, 2), (12422, 9, 2), (12422, 15, 2),
(12422, 4, 7), (12422, 10, 7), (12422, 16, 7),
(12422, 5, 9), (12422, 11, 9), (12422, 17, 9),
(12422, 6, 3), (12422, 12, 3), (12422, 18, 3)
We then use the recursive CTE to get calculate our data. It stores these in table #StockProjections.
What this does is
Start with your current stock (last row in the #Stock table). Note that the only value that matters in that is the stock at end of month.
Uses that stock level at the end of last month, as the stock level at the start of the new month
Adds the safety stock, minuses the NFP, and calculates your stock at end.
Note that within the recursive part of the CTE, 'SBM' (StockByMonth) refers to last month's data). This is then used with whatever external data (e.g., #NFP) to calculate new data.
These calculations create a table with
StockNum (the ID number of the relevant stock item - for this example, I've used one stock item 12422)
MonthNum (I've used integers this rather than dates, for clarity/simplicity)
BucketName (an nvarchar representing the month, used for column names)
Stock at start of month
Safety stock (which I assume is incoming stock, 5 per month)
NFP (which I assume is outgoing stock, varies by month and comes from a scratch table here - you'll need to adjust this to your select)
Stock at end of month
/* CALCULATE PROJECTIONS */
CREATE TABLE #StockProjections (StockNum int, BucketName nvarchar(30), MonthNum int, StockAtStart int, SafetyStock int, NFP int, StockAtEnd int, PRIMARY KEY (StockNum, BucketName))
; WITH StockByMonth AS
(-- Anchor
SELECT TOP 1 StockNum, MonthNum, StockAtStart, SafetyStock, NFP, StockAtEnd
FROM #Stock S
ORDER BY MonthNum DESC
-- Recursion
UNION ALL
SELECT NFP.StockNum,
SBM.MonthNum + 1 AS MonthNum,
SBM.StockAtEnd AS NewStockAtStart,
5 AS Safety_Stock,
NFP.StockNFP,
SBM.StockAtEnd + 5 - NFP.StockNFP AS NewStockAtEnd
FROM StockByMonth SBM
INNER JOIN #NFP_by_month NFP ON NFP.MonthNum = SBM.MonthNum + 1
WHERE NFP.MonthNum <= 18
)
INSERT INTO #StockProjections (StockNum, BucketName, MonthNum, StockAtStart, SafetyStock, NFP, StockAtEnd)
SELECT StockNum, BucketName, MonthNum, StockAtStart, SafetyStock, NFP, StockAtEnd
FROM StockByMonth
INNER JOIN #RepBuckets ON StockByMonth.MonthNum = #RepBuckets.BucketNum
Now we have the data, we set up a table for reporting purposes. Note that this table has the month names embedded into the column names (e.g., StockAtEnd_Jun_21). It would be easier to use a generic name (e.g., StockAtEnd_Month4) but I've gone for the slightly more complex case here for demonstration.
/* SET UP TABLE FOR REPORTING */
DECLARE #cols int = 18
DECLARE #iteration int = 0
DECLARE #colname nvarchar(30)
DECLARE #sql2 as nvarchar(max)
CREATE TABLE #tmp (StockNum int PRIMARY KEY)
WHILE #iteration <= #cols
BEGIN
SET #colname = (SELECT TOP 1 BucketName FROM #RepBuckets WHERE BucketNum = #iteration)
SET #sql2 = 'ALTER TABLE #tmp ADD ' + QUOTENAME(#colname) + ' int'
EXEC (#sql2)
SET #iteration = #iteration + 1
END
The last step is to add the data to your reporting table. I've used a pivot here but feel free to use whatever you like.
/* POPULATE TABLE */
DECLARE #columnList nvarchar(max) = N'';
SELECT #columnList += QUOTENAME(BucketName) + N' ' FROM #RepBuckets
SET #columnList = REPLACE(RTRIM(#columnList), ' ', ', ')
DECLARE #sql3 nvarchar(max)
SET #sql3 = N'
;WITH StockPivotCTE AS
(SELECT *
FROM (SELECT StockNum, BucketName, StockAtEnd
FROM #StockProjections
) StockSummary
PIVOT
(SUM(StockAtEnd)
FOR [BucketName]
IN (' + #columnList + N')
) AS StockPivot
)
INSERT INTO #tmp (StockNum, ' + #columnList + N')
SELECT StockNum, ' + #columnList + N'
FROM StockPivotCTE'
EXEC (#sql3)
Here's a DB<>fiddle showing it running with results of each sub-step.

How Columns_update() Function Works in Trigger To Find Updated Columns Name

I am confused about trigger function Columns_Update().
I have a basic idea about this function that it returns a varbinary and it is use to get updated columns in trigger
Even I have find the code to get updated columns name but have still confusion in bit representation. I would be very thankful if someone explains this function or gives reference
I got the following function
CREATE FUNCTION dbo.GenColUpdated
(#Col INT, #ColTotal INT)
RETURNS INT
AS
BEGIN;
DECLARE
#ColByte INT,
#ColTotalByte INT,
#ColBit INT;
-- Calculate Byte Positions
SET #ColTotalByte = 1 + ((#ColTotal-1) /8);
SET #ColByte = 1 + ((#Col-1)/8);
SET #ColBit = #Col - ((#ColByte-1) * 8);
RETURN Power(2, #ColBit + ((#ColTotalByte-#ColByte) * 8)-1);
END;
and use that function in trigger like this
set #ColUpdatedTemp = dbo.GenColUpdated(#ColCounter, #ColTotal) ;
If COLUMNS_UPDATED() & dbo.GenColUpdated(#ColCounter, #ColTotal) = #ColUpdatedTemp
I didn't understand this condition
dbo.GenColUpdated(#ColCounter,#ColTotal) =
#ColUpdatedTemp
it doesn't make Any sense to me .First you are assign the value and than Check it
You can check this example of microsoft
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'employeeData')
DROP TABLE employeeData;
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'auditEmployeeData')
DROP TABLE auditEmployeeData;
GO
CREATE TABLE dbo.employeeData (
emp_id int NOT NULL PRIMARY KEY,
emp_bankAccountNumber char (10) NOT NULL,
emp_salary int NOT NULL,
emp_SSN char (11) NOT NULL,
emp_lname nchar (32) NOT NULL,
emp_fname nchar (32) NOT NULL,
emp_manager int NOT NULL
);
GO
CREATE TABLE dbo.auditEmployeeData (
audit_log_id uniqueidentifier DEFAULT NEWID() PRIMARY KEY,
audit_log_type char (3) NOT NULL,
audit_emp_id int NOT NULL,
audit_emp_bankAccountNumber char (10) NULL,
audit_emp_salary int NULL,
audit_emp_SSN char (11) NULL,
audit_user sysname DEFAULT SUSER_SNAME(),
audit_changed datetime DEFAULT GETDATE()
);
GO
CREATE TRIGGER dbo.updEmployeeData
ON dbo.employeeData
AFTER UPDATE AS
/*Check whether columns 2, 3 or 4 have been updated. If any or all
columns 2, 3 or 4 have been changed, create an audit record. The
bitmask is: power(2,(2-1))+power(2,(3-1))+power(2,(4-1)) = 14. To test
whether all columns 2, 3, and 4 are updated, use = 14 instead of >0
(below).*/
IF (COLUMNS_UPDATED() & 14) > 0
/*Use IF (COLUMNS_UPDATED() & 14) = 14 to see whether all columns 2, 3,
and 4 are updated.*/
BEGIN
-- Audit OLD record.
INSERT INTO dbo.auditEmployeeData
(audit_log_type,
audit_emp_id,
audit_emp_bankAccountNumber,
audit_emp_salary,
audit_emp_SSN)
SELECT 'OLD',
del.emp_id,
del.emp_bankAccountNumber,
del.emp_salary,
del.emp_SSN
FROM deleted del;
-- Audit NEW record.
INSERT INTO dbo.auditEmployeeData
(audit_log_type,
audit_emp_id,
audit_emp_bankAccountNumber,
audit_emp_salary,
audit_emp_SSN)
SELECT 'NEW',
ins.emp_id,
ins.emp_bankAccountNumber,
ins.emp_salary,
ins.emp_SSN
FROM inserted ins;
END;
GO
/*Inserting a new employee does not cause the UPDATE trigger to fire.*/
INSERT INTO employeeData
VALUES ( 101, 'USA-987-01', 23000, 'R-M53550M', N'Mendel', N'Roland', 32);
GO
/*Updating the employee record for employee number 101 to change the
salary to 51000 causes the UPDATE trigger to fire and an audit trail to
be produced.*/
UPDATE dbo.employeeData
SET emp_salary = 51000
WHERE emp_id = 101;
GO
SELECT * FROM auditEmployeeData;
GO
/*Updating the employee record for employee number 101 to change both
the bank account number and social security number (SSN) causes the
UPDATE trigger to fire and an audit trail to be produced.*/
UPDATE dbo.employeeData
SET emp_bankAccountNumber = '133146A0', emp_SSN = 'R-M53550M'
WHERE emp_id = 101;
GO
SELECT * FROM dbo.auditEmployeeData;
GO

Stored Procedure Using IF Statement then Create Virtual Table for Pick up Data

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[SFC_spViewPO_Select]
(#trans int = NULL)
as
begin
IF #trans = 1
DECLARE #AutoRR TABLE
(
spPONumber nvarchar (30)
,spItemnmbr nvarchar (20)
,spItemDesc nvarchar (65)
,spUofM nvarchar (20)
,spLocncode nvarchar (20)
,spUnitCost float
,spPurchase float
,spReceive float
,spReceiveTmp float
,spOrd int
,spCancel float
,spVendID nvarchar (20)
)
BEGIN
INSERT INTO #AutoRR
(
spPONumber,
spItemnmbr,
spItemDesc,
spUofM,
spLocncode,
spUnitCost,
spPurchase,
spReceive,
spReceiveTmp,
spOrd,
spVendID
)
SELECT PONUMBER,
ITEMNMBR,
ITEMDESC,
UOFM,
LOCNCODE,
CASE WHEN Adjustedprice=0 then UNITCOST else AdjustedPrice end,
QTYORDER-QTYCANCE,
0,
0,
ord,
VendorID
FROM POP10110
WHERE (VENDORID ='KORGRO01 ') AND
EXISTS(Select * from pop10100 where (Ponumber = POP10110.PoNumber) AND
(POSTSTATUS=1) AND
(POSTATUS < 4) AND
(Revision_Number <> 1))
INSERT INTO #AutoRR
(
spPONumber,
spItemnmbr,
spItemDesc,
spUofM,
spLocncode,
spUnitCost,
spPurchase,
spReceive,
spReceiveTmp,
spOrd,
spVendID
)
SELECT POP10110.PONumber,
POP10110.ITEMNMBR,
POP10110.ITEMDESC,
POP10110.UOFM,
POP10110.LOCNCODE,
CASE WHEN POP10110.Adjustedprice=0 then POP10110.UNITCOST else POP10110.AdjustedPrice end,
0,
SUM(POP10500.QTYSHPPD),
0,
POP10110.Ord,
POP10110.VendorID
FROM POP10110 INNER JOIN
POP10500 ON POP10110.PONUMBER = POP10500.PONUMBER AND POP10110.ORD = POP10500.POLNENUM
WHERE (POP10110.VendorID='KORGRO01 ') AND
EXISTS(Select * from pop10100 where (Ponumber = POP10500.PoNumber) AND
(POSTSTATUS=1) AND
(POSTATUS < 4) AND
(Revision_Number <> 1))
GROUP BY POP10110.ITEMNMBR,
POP10110.ITEMDESC,
POP10110.UOFM,
POP10110.LOCNCODE,
POP10110.UNITCOST,
POP10110.PONumber,
POP10110.Ord,
POP10110.Adjustedprice,
POP10110.VendorID
INSERT INTO #AutoRR
(
spPONumber,
spItemnmbr,
spItemDesc,
spUofM,
spLocncode,
spUnitCost,
spPurchase,
spReceive,
spReceiveTmp,
spOrd,
spVendID
)
SELECT ponumber,
Itemnmbr,
ItemDesc,
UofM,
Locncode,
UnitCost,
0,
0,
Quantity,
ord,
VendorID
FROM POPTEMPRECV
WHERE VendorID='KORGRO01 '
SELECT spPONumber as ponumber,
spItemnmbr as itemnmbr,
spItemDesc as itemdesc,
spUofM as uofm,
spLocncode as locncode,
spUnitCost as UnitCost,
SUM(spPurchase) - (SUM(spReceive) + SUM(spReceiveTmp)) as Balance,
spORd as ORD,
SUM(spPurchase) AS TotalPO,
spVendID
FROM #AutoRR
WHERE spVendID = 'KORGRO01 '
GROUP BY spPONumber,
spItemnmbr,
spItemDesc,
spUofM,
spLocncode,
spUnitCost,
spOrd,
spVendID
HAVING SUM(spPurchase) - (SUM(spReceive) + SUM(spReceiveTmp)) > 0
END
ELSE IF #trans = 2
DECLARE #AutoRRII TABLE
(
spPONumber nvarchar (30)
,spItemnmbr nvarchar (20)
,spItemDesc nvarchar (65)
,spUofM nvarchar (20)
,spLocncode nvarchar (20)
,spUnitCost float
,spPurchase float
,spReceive float
,spReceiveTmp float
,spOrd int
,spCancel float
,spVendID nvarchar (20)
)
BEGIN
-- ANOTHER STORE PROCEDURE WITH DIFFERENT CREATED VIRTUAL TABLE...
END
END
Tried doing this method but it won't work on c# after calling the parameters on my stored procedure virtual table.. I was thinking of maybe it would look like this on my c# codes after connecting them.
SFC_spViewPO_Select(1, parameter1, parameter2, parameter3.. etc..) then another for
SFC_spViewPO_Select(2, parameter1, parameter2, parameter3.. etc..).. the numbers that is found 1st at the parameters of the said stored procedure should indicate of the IF statement i set in the SQL Server stored procedure which then follows up the parameters for my virtual table I made inside the if statement conditions is this possible I have been trying to figure out how this thing works but never get one to function well yet.. :( help!
You need to move the declare table statements either outside of the if block (before IF), or inside the IF block after the BEGIN statement.

Error trying to insert data using trigger T-SQL

Below is a trigger used on one of our SQL tables for any insert/update action. 99/100 times this trigger works just fine however every now and then we receive this error message:
Cannot insert the value NULL into column 'TransactionDate', table
'AgentResourcesU01.dbo.TransactionLog'; column does not allow nulls.
INSERT fails. The statement has been terminated.
As you can see from the Insert statement, the columns in our transaction log table are TransactionDate, Operator, TableName, Action, TableString and UserId. I set the variable #transDate in the opening SELECT statement so as it appears to me, there should be no way a NULL gets in there unless it's bad data coming in.
Any thoughts?
BEGIN
SELECT #symetraNumber = SymetraNumber, #lastChangeOperator = LastChangeOperator, #transDate = LastChangeDate, #entityType = EntityType,
#firstName = FirstName, #lastName = LastName, #suffix = NameSuffix, #corpName = CorporateName, #controlId = ControlId
FROM inserted
IF #firstName IS NULL SET #firstName = 'NULL'
IF #lastName IS NULL SET #lastName = 'NULL'
IF #suffix IS NULL SET #suffix = 'NULL'
IF #corpName IS NULL SET #corpName = 'NULL'
IF #controlId IS NULL SET #controlId = 'NULL'
SET #tableString = 'SymNum:' + #symetraNumber + ' EntType:' + #entityType + ' Fname:' + #firstName + ' Lname:' + #lastname + ' Suff:' + #suffix +
' CorpName:' + #corpName + ' ctrlId:' + #controlId
INSERT INTO TransactionLog (TransactionDate, Operator, TableName, Action, TableString, UserId)
VALUES (#transDate, 'Op', #tableName, #action, #tableString, #lastChangeOperator)
END
To demonstrate Marc's point, you can do this in a set-based way, without all these nasty variables and IF checks:
INSERT dbo.TransactionLog
(
TransactionDate,
Operator,
TableName,
Action,
TableString,
UserId
)
SELECT
LastChangeDate,
'Op',
#TableName,
#action,
'SymNum:' + COALESCE(SymetraNumber, 'NULL')
+ ' EntType:' + COALESCE(EntityType, 'NULL')
+ ' Fname:' + COALESCE(FirstName, 'NULL')
+ ' Lname:' + COALESCE(LastName, 'NULL')
+ ' Suff:' + COALESCE(NameSuffix, 'NULL')
+ ' CorpName:' + COALESCE(CorporateName, 'NULL')
+ ' ctrlId:' + COALESCE(ControlId, 'NULL'),
LastChangeOperator
FROM inserted;
If LastChangeDate in the underlying table is NULLable, either mark it as NOT NULL, fix the problem where NULL is getting inserted, or both. The trigger shouldn't have to know about this constraint but you can work around it by doing something like this (if the value is NULL, set it to right now):
...
UserId
)
SELECT
COALESCE(LastChangeDate, CURRENT_TIMESTAMP),
'Op',
...
[1] I assume that you have an INSERT/UPDATE/DELETE trigger and when somebody try to delete rows from the base table then the inserted table from trigger will have zero rows. Look at this example:
CREATE TABLE MyTableWithTrigger (
MyID INT IDENTITY PRIMARY KEY,
LastUpdateDate DATETIME NOT NULL
);
GO
CREATE TABLE TransactionLog (
TransactionLogID INT IDENTITY PRIMARY KEY,
CreateDate DATETIME NOT NULL DEFAULT GETDATE(),
LastUpdateDate DATETIME NOT NULL,
MyID INT NOT NULL
);
GO
CREATE TRIGGER trIUD_MyTableWithTrigger_Audit
ON MyTableWithTrigger
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
DECLARE #LastUpdateDate DATETIME, #MyID INT;
SELECT #LastUpdateDate=i.LastUpdateDate, #MyID=i.MyID
FROM inserted i;
INSERT TransactionLog (LastUpdateDate, MyID)
VALUES (#LastUpdateDate, #MyID)
END;
GO
PRINT 'Test 1'
INSERT MyTableWithTrigger (LastUpdateDate)
VALUES ('2011-01-01');
DELETE MyTableWithTrigger;
SELECT * FROM MyTableWithTrigger;
SELECT * FROM TransactionLog;
Output:
Msg 515, Level 16, State 2, Procedure trIUD_MyTableWithTrigger_Audit, Line 10
Cannot insert the value NULL into column 'LastUpdateDate', table 'Test.dbo.TransactionLog'; column does not allow nulls. INSERT fails.
The statement has been terminated.
[2] To correct your trigger (see Marc's comment) please read pages 192 - 199 from Alex Kuznetsov's book (Defensive Database Programming with SQL Server: Amazon, Red Gate - PDF).

Use master.sys.fn_varbintohexsubstring in computed column

In my sql server table, I want to add a computed column that is a hash of other columns in the same table. Below is my table structure.
Address:
AddressID(int , PK)
AddressLine1 (nvarchar)
AddressLine2 (nvarchar)
City (nvarchar)
State (nvarchar)
AddressHash(computed column)
Below is what I want to get in my computed column:
MASTER.SYS.FN_VARBINTOHEXSUBSTRING(0, HASHBYTES('SHA1',COALESCE(AddressLine1, N'') + COALESCE(AddressLine2, N'') + COALESCE(City, N'') + COALESCE(State, N'')), 1, 0)
If I right-click the table and go to design and enter the above for "Formula" under "Computed Column Specification", I get the following error:
- Unable to modify table.
A user-defined function name cannot be prefixed with a database name in this context.
So I thought I would use a user defined function to calculate the hash and map that udf to formula.
Below is the code that I am using to create UDF:
CREATE FUNCTION udfHashAddress
(
#pAddressLine1 nvarchar(50), #pAddressLine2 nvarchar(50), #pCity nvarchar(50), #pState nvarchar(50))
)
RETURNS nvarchar(max) -- not sure what the correct size would be
WITH SCHEMABINDING
AS
BEGIN
DECLARE #result nvarchar(max)
SELECT #result = MASTER.SYS.FN_VARBINTOHEXSUBSTRING(0, HASHBYTES('SHA1',COALESCE(#pAddressLine1, N'') + COALESCE(#pAddressLine2, N'') + COALESCE(#pCity, N'') + COALESCE(#pState, N'')), 1, 0)
RETURN #result
END
GO
But I get the following error with the above code:
*Cannot schema bind function 'udfHashAddress' because name 'MASTER.SYS.FN_VARBINTOHEXSUBSTRING' is invalid for schema binding. Names must be in two-part format and an object cannot reference itself.*
When I removed the "MASTER" db prefix I got this error:
*Cannot schema bind function 'udfHashAddress' because it references system object 'SYS.FN_VARBINTOHEXSUBSTRING'.*
Am I missing something here? Would appreciate any assistance/pointers.
Since you're using SQL Server 2008, have you tried simply:
CONVERT(VARCHAR(MAX), HASHBYTES('SHA1','string'), 2);
This will return upper case instead of lower case letters, but you can fix that with LOWER() if it's important.
Here is an example with your actual schema (created in tempdb on purpose):
USE tempdb;
GO
CREATE TABLE dbo.[Address]
(
AddressID INT PRIMARY KEY,
AddressLine1 NVARCHAR(64),
AddressLine2 NVARCHAR(64),
City NVARCHAR(64),
[State] NVARCHAR(64),
AddressHash AS LOWER(CONVERT(VARCHAR(4000), HASHBYTES('SHA1',
COALESCE(AddressLine1, N'') + COALESCE(AddressLine2, N'')
+ COALESCE(City, N'') + COALESCE([State], N'')), 2))
--PERSISTED -- you could also persist it if desired
);
INSERT dbo.[Address]
VALUES(1, 'foo', 'bar', 'blat', 'splunge'),
(2, 'bar', 'foo', 'blag', 'splmger');
SELECT *, master.dbo.fn_varbintohexsubstring
(0,
HASHBYTES
(
'SHA1',
COALESCE(AddressLine1, N'') + COALESCE(AddressLine2, N'')
+ COALESCE(City, N'') + COALESCE([State], N'')
), 1, 0)
FROM dbo.[Address];
GO
DROP TABLE dbo.[Address];

Resources