Update statement in stored procedure with varbinary parameter - sql-server

I have the following table:
CREATE TABLE NZQA_Unit(
NZQAUnitID int IDENTITY(1,1) PRIMARY KEY NOT NULL,
NZQAUnitNumber int NOT NULL,
Title nvarchar(255) NOT NULL,
Level smallint NOT NULL,
Credits smallint NOT NULL,
Classification nvarchar(255) NULL,
AvailableGrade int NULL DEFAULT 1,
Purpose nvarchar(max) NULL,
Validity int NOT NULL DEFAULT 1, -- by default the unit will be 'Current'
Document VARBINARY(MAX) NULL, -- for being able to upload a PDF or Word document
DocumentExtension varchar(5) NULL, -- for storing the file extension
CONSTRAINT AK_NZQA_Unit_Title UNIQUE(Title),
CONSTRAINT AK_NZQA_Unit_Number UNIQUE(NZQAUnitNumber),
CONSTRAINT FK_NZQA_Unit_Validity FOREIGN KEY (Validity) REFERENCES NZQA_Unit_Validity (ValidityID),
CONSTRAINT FK_NZQA_Unit_AvailableGrade FOREIGN KEY (AvailableGrade) REFERENCES NZQA_Unit_Assessment_Grade (AssessmentGradeID),
CONSTRAINT CK_NZQA_Unit_Title CHECK ((len(ltrim(rtrim(Title)))>(2))),
CONSTRAINT CK_NZQA_Unit_ID_Range CHECK (NZQAUnitNumber >= 1000 AND NZQAUnitNumber <= 99999), --Inclusive
CONSTRAINT CK_NZQA_Unit_Level CHECK (Level >= 1 AND Level <= 10), -- Level must be between 1 and 10 https://www.nzqa.govt.nz/studying-in-new-zealand/understand-nz-quals/
CONSTRAINT CK_NZQA_Unit_Credits CHECK (Credits >= 1 AND Credits <= 999), -- 999 has been set arbitrarily but it's high enough to fit an Engineering Degree
CONSTRAINT CK_NZQA_Unit_DocumentSize CHECK (DATALENGTH(Document) <= 524288), -- Maximum size 500 KB https://stackoverflow.com/questions/34741079/can-i-set-2-mb-for-maximum-size-of-varbinary https://www.gbmb.org/mb-to-bytes
CONSTRAINT CK_NZQA_Unit_DocumentExtension CHECK (DocumentExtension IN ('.pdf', '.doc', '.docx')) -- this check is not case sensitive, i.e. '.DOCX' won't trigger an error
);
GO
And I'm writing the following stored procedure:
DROP PROCEDURE IF EXISTS Modify_NZQA_Unit
GO
CREATE PROCEDURE Modify_NZQA_Unit
#NZQAUnitID int,
#NZQAUnitNumber int,
#Title nvarchar(255),
#Level smallint,
#Credits smallint,
#Classification nvarchar(255) NULL,
#AvailableGrade int,
#Purpose nvarchar(max),
#Validity int,
#Document VARBINARY(MAX),
#DocumentExtension varchar(5),
#overwriteFile bit -- 1 to overwrite, 0 to no overwrite
AS
BEGIN
IF (#NZQAUnitID IS NULL)
BEGIN
THROW 51006, 'You must input the NZQA identifier (NZQAUnitID)', 1;
END
SET #Title = Replace(#Title, '''', '''''') -- singles quotes must be escaped
SET #Classification = Replace(#Classification, '''', '''''')
SET #Purpose = Replace(#Purpose, '''', '''''')
SET #DocumentExtension = Replace(#DocumentExtension, '''', '''''')
DECLARE #updateStatement AS NVARCHAR(1000);
SET #updateStatement = 'UPDATE NZQA_Unit SET NZQAUnitNumber = '+CONVERT(VARCHAR, #NZQAUnitNumber)+', Title = '''+#Title+''', Level = '+CONVERT(VARCHAR, #Level)+', Credits = '+CONVERT(VARCHAR, #Credits)+', Classification = '''+#Classification+''', AvailableGrade = '+CONVERT(VARCHAR, #AvailableGrade)+', Purpose = '''+#Purpose+''', Validity = '+CONVERT(VARCHAR, #Validity)
IF (#overwriteFile IS NULL)
BEGIN
THROW 51007, 'Variable #overwriteFile cannot be null', 1;
END
ELSE
BEGIN
IF (#overwriteFile = 1)
BEGIN
IF (#Document IS NULL)
BEGIN
THROW 51008, 'If the variable #overwriteFile is set to 1, a file (#Document) must be provided', 1;
END
IF (#DocumentExtension IS NULL)
BEGIN
THROW 51009, 'If the variable #overwriteFile is set to 1, the document extension (#DocumentExtension) must be provided', 1;
END
SET #updateStatement = #updateStatement + ', Document = '+'HERE WILL COME THE VARBINARY'+', DocumentExtension = '''+#DocumentExtension + ''' '
--DOESN'T WORK: EXEC('UPDATE NZQA_Unit SET NZQAUnitNumber = '+#NZQAUnitNumber+', Title = '''+#Title+''', Level = '+#Level+', Credits = '+#Credits+', Classification = '''+#Classification+''', AvailableGrade = '+#AvailableGrade+', Purpose = '''+#Purpose+''', Validity = '+#Validity + ', Document = ' + #document + ', DocumentExtension = '''+#DocumentExtension + ''' ' +' WHERE NZQAUnitID = '+#NZQAUnitID)
UPDATE NZQA_Unit SET NZQAUnitNumber = #NZQAUnitNumber, Title = #Title, Level = #Level, Credits = #Credits, Classification = #Classification, AvailableGrade = #AvailableGrade, Purpose = #Purpose, Validity = #Validity, Document = #document, DocumentExtension = #DocumentExtension WHERE NZQAUnitID = #NZQAUnitID
END
ELSE
BEGIN
UPDATE NZQA_Unit SET NZQAUnitNumber = #NZQAUnitNumber, Title = #Title, Level = #Level, Credits = #Credits, Classification = #Classification, AvailableGrade = #AvailableGrade, Purpose = #Purpose, Validity = #Validity WHERE NZQAUnitID = #NZQAUnitID
END
END
SET #updateStatement = #updateStatement +' WHERE NZQAUnitID = '+CONVERT(VARCHAR, #NZQAUnitID)
PRINT #updateStatement
END
GO
How could I insert the varbinary data (#Document) into the #updateStatement variable? That way I could simply do a EXEC(#updateStatement)?
Code to execute the procedure below:
DECLARE #current AS INT = 1
DECLARE #expiring AS INT = 2
DECLARE #expired AS INT = 3
DECLARE #achieved AS INT = 1
DECLARE #datos AS VARBINARY(30) = CONVERT(varbinary(30), N'this IS a test')
INSERT NZQA_Unit (NZQAUnitNumber, [Title], [Level], [Credits], [Classification], [AvailableGrade], [Purpose], Validity) VALUES (6401, N'Provide first aid', 2, 1, N'Health Studies > First Aid', #achieved, N'People credited with this unit standard are able to provide first aid.', #current)
EXEC Modify_NZQA_Unit 1, 6424, N'Provide first aid', 2, 1, N'Health Studies > First Aid', #achieved, N'People credited with this unit standard are able to provide first aid.', #current, #datos, '.pdf', 1

Related

Ms Sql Trigger Permission

This trigger was not written by me or used with a stored proc original I modified it for my use. The problem to me appears to be one of permissions when the table is manually updated or inserted inSQL Server Management Studio the trigger works perfectly. When the Trigger is updated or inserted by a Stored Proc ran via an ODBC connection it does not modify the table and the Trigger does not work. If the trigger is disabled the stored Proc works as expected. So the error appers to be some form of permission error. Here is the trigger. (Using 2012)
Thanks In Advance
Donald S. Bossen
USE [JBI]
GO
/****** Object: Trigger [dbo].[send_ship_exec_info_to_p21] Script Date: 8/29/2018 3:22:26 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[send_ship_exec_info_to_p21] ON [dbo].[ship_exec]
FOR INSERT, UPDATE
AS
declare #pick_ticket_no varchar(50)
declare #tracking_no varchar(50)
declare #pickup_date varchar(50)
declare #carrier_id varchar(50)
declare #package_count varchar(50)
declare #delete_flag varchar(50)
declare #package_weight varchar(50)
declare #package_charge decimal(9,4)
declare #total_shipment_charge decimal(19,4)
declare #carrier_name varchar(50)
declare #customer_freight_charge decimal(9,4)
declare #saturday_delivery varchar(50)
declare #transaction_type varchar(50)
declare #billing_option varchar(3)
select
#pick_ticket_no = i.pick_ticket_no,
#tracking_no = i.tracking_no,
#pickup_date = i.pickup_date,--left(i.pickup_date,8),
#carrier_id = i.service_type,
#package_count = i.package_count,
#delete_flag = i.delete_flag,
#package_weight = i.package_weight,
#package_charge = i.package_charge,
#total_shipment_charge = i.total_shipping_and_handling_charge,
#saturday_delivery = i.saturday_delivery,
#transaction_type = i.transaction_type,
#billing_option = i.billing_option
from inserted i
if #transaction_type = 'Inv Return'
begin
declare #document_link_uid int
declare #document_link_area_uid int
declare #link_name varchar(255)
declare #link_path varchar(4099)
set #link_name = 'UPS shipment ' + cast(#pick_ticket_no as varchar(40)) + ': ' + #tracking_no
set #link_path = 'http://wwwapps.ups.com/WebTracking/processInputRequest?HTMLVersion=5.0&sort_by=status&term_warn=yes&tracknums_displayed=5&TypeOfInquiryNumber=T&loc=en_US&InquiryNumber1=' + #tracking_no + '&InquiryNumber2=&InquiryNumber3=&InquiryNumber4=&InquiryNumber5=&AgreeToTermsAndConditions=yes&track.x=28&track.y=2'
if #delete_flag = 'Y'
begin
update
PROPHET21.dbo.document_link
set
row_status_flag = '700'
,date_last_modified = getdate()
,last_maintained_by = 'Ship Exec Delete Flag'
where
key1_cd = 'return_number'
and key1_value = #pick_ticket_no
and link_path like '%' + #tracking_no + '%'
end
if #delete_flag = 'N'
begin
if not exists(select document_link_uid from PROPHET21.dbo.document_link where link_path like '%' + #tracking_no + '%')
begin
exec #document_link_uid = PROPHET21.dbo.p21_get_counter 'document_link', 1
INSERT INTO
PROPHET21.dbo.document_link
(
document_link_uid
,source_area_cd
,key1_cd
,key1_value
,link_name
,link_path
,row_status_flag
,date_created
,created_by
,date_last_modified
,last_maintained_by
,outside_use_flag
,mandatory_flag
)
VALUES
(
#document_link_uid
,1312
,'return_number'
,#pick_ticket_no
,#link_name
,#link_path
,704
,getdate()
,'SHIP EXEC'
,getdate()
,'SHIP EXEC'
,'N'
,'N'
)
exec #document_link_area_uid = PROPHET21.dbo.p21_get_counter 'document_link_area', 1
INSERT INTO
PROPHET21.dbo.document_link_area
(
document_link_area_uid
,document_link_uid
,display_area_cd
,row_status_flag
,date_created
,created_by
,date_last_modified
,last_maintained_by
)
VALUES
(
#document_link_area_uid
,#document_link_uid
,1312
,704
,getdate()
,'SHIP EXEC'
,getdate()
,'SHIP EXEC'
)
end
end
end
if #transaction_type <> 'SWS' or not exists(select oe_pick_ticket.pick_ticket_no from PROPHET21.dbo.oe_pick_ticket oe_pick_ticket where oe_pick_ticket.pick_ticket_no = #pick_ticket_no)
return
if #transaction_type = 'SWS' and exists(select oe_pick_ticket.pick_ticket_no from PROPHET21.dbo.oe_pick_ticket oe_pick_ticket where oe_pick_ticket.pick_ticket_no = #pick_ticket_no)
begin
if (#delete_flag = 'Y')
begin
declare #invoiced_already varchar (50)
select #invoiced_already = oe_pick_ticket.invoice_no
from PROPHET21.dbo.oe_pick_ticket oe_pick_ticket
where pick_ticket_no = #pick_ticket_no
if(#invoiced_already is null)
begin
update PROPHET21.dbo.clippership_return_10004
set delete_flag = 'Y'
where tracking_no = #tracking_no
update PROPHET21.dbo.oe_pick_ticket
set freight_out = 0, date_last_modified = getdate(), last_maintained_by = 'Ship Exec void package'
where pick_ticket_no = #pick_ticket_no
end
return
end
if (#delete_flag = 'N')--#tracking_no like '1z%' and
Begin
declare #customer_pays_outgoing_freight char(1)
select
#customer_pays_outgoing_freight = freight_code.outgoing_freight
from
PROPHET21.dbo.oe_pick_ticket oe_pick_ticket
inner join PROPHET21.dbo.freight_code freight_code on oe_pick_ticket.freight_code_uid = freight_code.freight_code_uid and oe_pick_ticket.company_id = freight_code.company_id
where
oe_pick_ticket.pick_ticket_no = #pick_ticket_no
select
#carrier_name = carrier.name
from
PROPHET21.dbo.address carrier
where
carrier.id = #carrier_id
select #customer_freight_charge =
cast(case
when #billing_option in ('REC', 'TP', 'CB') then 0
when coalesce(#customer_pays_outgoing_freight, 'Y') = 'N' then 0
else ((#total_shipment_charge) / #package_count)
end as decimal(19,2))
update PROPHET21.dbo.oe_pick_ticket
set freight_out = isnull(freight_out, 0) + isnull(#customer_freight_charge, 0),
carrier_id = #carrier_id,
tracking_no = #tracking_no
where pick_ticket_no = #pick_ticket_no
insert into PROPHET21.dbo.clippership_return_10004
(pick_ticket_no,
tracking_no,
package_weight,
order_count,
shipped_date,
carrier_name,
total_charge,
processed_flag,
delete_flag,
date_created,
date_last_modified,
last_maintained_by,
line_number)
values
(#pick_ticket_no,
#tracking_no,
#package_weight,
#package_count,
#pickup_date,
#carrier_name,
#customer_freight_charge,
'N',
'N',
getdate(),
getdate(),
'SHIP EXEC',
0)
end
return
End

MicrosoftSQL: How to create a table inside a Trigger and insert values

im trying to create a trigger on a table with the follow characteristics
Whenever and UPDATE is used on Project_Ypalliloi (table name) i would like to Create another Table (for instance deleted_Ypalliloi)
I would like to take the deleted line, and insert it into the new Table
this is my table:
create table Project_Ypalliloi
(
arithmos_taut int primary key not null,
onoma varchar(20)not null,
eponymo varchar(20)not null,
imerominia_proslipsis date not null,
imerominia_gennisis date not null,
misthos float
)
this is my trigger:
CREATE TRIGGER deleteTrigger ON Project_Ypalliloi FOR DELETE AS --errorline1
DECLARE #arithmos_taut int
DECLARE #onoma varchar(20)
DECLARE #eponymo varchar(20)
DECLARE #imerominia_proslipsis date
DECLARE #imerominia_gennisis date
DECLARE #misthos float
DECLARE #getnamesCursor CURSOR
SET #getnamesCursor = CURSOR FOR
SELECT arithmos_taut,onoma,eponymo,imerominia_proslipsis,imerominia_gennisis,misthos FROM Project_Ypalliloi --where How can i get the deleted line?(under what condition?)
OPEN #getnamesCursor
FETCH NEXT FROM #getnamesCursor INTO #arithmos_taut,#onoma,#eponymo,#imerominia_proslipsis,#imerominia_gennisis,#misthos
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO deleted_Ypalliloi Values '('+rtrim(#arithmos_taut) + ',' + rtrim(#onoma) + ',' + rtrim(#eponymo) + ' ,' + rtrim(#imerominia_proslipsis) + ',' + rtrim(#imerominia_gennisis) + ', ' + rtrim(#misthos)+')'
--FETCH NEXT FROM #getnamesCursor INTO #c_name,#c_surname
END --errorline 2
CLOSE #getnamesCursor
DEALLOCATE #getnamesCursor
My trigger code is in the Query,and i get an error:"Incorrect Syndax at errorline 1 and errorline 2
Thanks a lot for your help
USE "FROM DELETED"
CREATE TRIGGER deleteTrigger ON Project_Ypalliloi AFTER DELETE
AS
BEGIN
DECLARE #arithmos_taut int;
DECLARE #onoma varchar(20);
DECLARE #eponymo varchar(20);
DECLARE #imerominia_proslipsis date;
DECLARE #imerominia_gennisis date;
DECLARE #misthos float;
SELECT #arithmos_taut = arithmos_taut,
#onoma = onoma, #eponymo = eponymo,
#imerominia_proslipsis = imerominia_proslipsis,
#imerominia_gennisis = imerominia_gennisis,
#misthos = misthos FROM DELETED;
INSERT INTO deleted_Ypalliloi
VALUES(#arithmos_taut, #onoma, #eponymo, #imerominia_proslipsis, #imerominia_gennisis, #misthos);
END
For two or more deleted rows
INSERT INTO deleted_Ypalliloi SELECT * FROM DELETED;

Reset the ID counter on a stored procedure in SQL Server

I'm developing a system that manages work orders for vehicles. The ID of work orders is composed as follows: OT-001-16.
Where OT- is a string, 001 is the counter, followed by - character and finally the number 16 is the current year.
Example:
If the current year is 2018, the ID should be OT-001-18.
The problem is when the year changes, the counter must restart from 001. I have a stored procedure to do that, but i think i'm doing a lot more work.
This is my stored procedure code:
CREATE PROCEDURE ot (#name varchar(100), #area varchar(100), #idate varchar(100), #edate varchar(100))
AS
BEGIN
SET NOCOUNT ON;
DECLARE #aux varchar(100);
DECLARE #aux2 varchar(100);
DECLARE #aux3 int;
DECLARE #aux4 varchar(100);
SELECT #aux = id_workorder FROM idaux;
IF (#aux IS NULL)
SET #aux = CONCAT('OT-000-', RIGHT(YEAR(GETDATE()), 2));
SET
#aux2 = SUBSTRING(
#aux, CHARINDEX('-', #aux) + 1,
LEN(#aux) - CHARINDEX('-', #aux) - CHARINDEX('-', REVERSE(#aux)));
SET #aux3 = CAST(#aux2 AS int) + 1;
SET #aux4 = #aux3;
IF #aux3 < 1000
IF #aux3 >= 10
SET #aux4 = CONCAT('0', #aux4);
ELSE
SET #aux4 = CONCAT('00', #aux4);
ELSE
SET #aux4 = #aux4;
DECLARE #f varchar(100);
DECLARE #y varchar(50);
SELECT TOP 1
#y = id_workorder
FROM workorder
WHERE (RIGHT(id_workorder, 2)) = (RIGHT(YEAR(GETDATE()), 2))
ORDER BY id_workorder DESC;
DECLARE #yy varchar(10);
SET
#yy = RIGHT(#y, 2);
DECLARE #yn varchar(10);
SET
#yn = RIGHT(YEAR(GETDATE()), 2);
BEGIN
IF #yn = #yy
BEGIN
DECLARE #laux varchar(20)
SET #f = 'OT-' + #aux4 + '-' + RIGHT(YEAR(GETDATE()), 2);
INSERT INTO workorder (id_workorder, name, area, initial_date, end_date)
VALUES (#f, #name, #area, #idate, #edate);
SELECT
#laux = id_workorder
FROM idaux
IF (#laux IS NULL)
BEGIN
INSERT idaux (id_workorder) VALUES (#f);
END
ELSE
BEGIN
UPDATE idaux SET id_workorder = #f;
END
END
ELSE
BEGIN
SET #f = CONCAT('OT-001-', (RIGHT(YEAR(GETDATE()), 2)));
INSERT INTO workorder (id_workorder, name, area, initial_date, end_date)
VALUES (#f, #name, #area, #idate, #edate);
SELECT #laux = id_workorder FROM idaux;
IF (#laux IS NULL)
BEGIN
INSERT idaux (id_workorder) VALUES (#f);
END
ELSE
BEGIN
UPDATE idaux SET id_workorder = #f;
END
END
END
END
Basically, i created an auxiliar table to save the last Work Order ID, then from this table called idaux i take the ID and i compared to new possible ID by a string handling. Then if the year of the last ID saved are equal to the current year the counter increases, but if not the counter is restarted to 001, the new ID is updated in the auxiliar table and the Work Order is inserted to the table workorder.
My stored procedure works, but i need your help to optimize the stored procedure. Any question post on comments.
Here is how I'd setup the stored procedure and the underlying table to keep track of your work orders:
create database tmpWorkOrders;
go
use tmpWorkOrders;
go
/*
The work order ID (as you wish to see it) and the
work order counter (per year) will be separated into
two separate columns (with a unique constraint).
The work order ID (you wish to see) is automatically
generated for you and stored "persisted":
http://stackoverflow.com/questions/916068/sql-server-2005-computed-column-is-persisted
*/
create table WorkOrders
(
SurrogateKey int identity(1, 1) primary key not null,
WorkOrderYear int not null,
WorkOrderCounter int not null,
WorkOrderID as
N'OT-' + right(N'000' + cast(WorkOrderCounter as nvarchar), 3)
+ N'-' + right(cast(WorkOrderYear as nvarchar), 2)persisted,
WorkOrderDescription nvarchar(200),
constraint UQ_WorkOrderIDs
unique nonclustered (WorkOrderYear, WorkOrderCounter)
);
go
create procedure newWorkOrder
(#WorkOrderYear int = null,
#WorkOderCounter int = null,
#WorkOrderDescription nvarchar(200) = null
)
as
begin
/*
If no year is given the the current year is assumed
*/
if #WorkOrderYear is null
begin
set #WorkOrderYear = year(current_timestamp);
end;
/*
If no work order counter (for the above year) is given
then the next available one will be given
*/
if #WorkOderCounter is null
begin
set #WorkOderCounter
= isnull(
(
select max(WorkOrderCounter)
from WorkOrders
where WorkOrderYear = #WorkOrderYear
) + 1,
0
);
end;
else
/*
If a work order counter has been passed to the
stored procedure then it must be validated first
*/
begin
/*
Does the work order counter (for the given year)
already exist?
*/
if exists
(
select 1
from dbo.WorkOrders as wo
where wo.WorkOrderYear = #WorkOrderYear
and wo.WorkOrderCounter = #WorkOderCounter
)
begin
/*
If the given work order counter already exists
then the next available one should be assigned.
*/
while exists
(
select 1
from dbo.WorkOrders as wo
where wo.WorkOrderYear = #WorkOrderYear
and wo.WorkOrderCounter = #WorkOderCounter
)
begin
set #WorkOderCounter = #WorkOderCounter + 1;
end;
end;
end;
/*
The actual insert of the new work order ID
*/
insert into dbo.WorkOrders
(
WorkOrderYear,
WorkOrderCounter,
WorkOrderDescription
)
values
(#WorkOrderYear,
#WorkOderCounter,
#WorkOrderDescription
);
end;
go
/*
Some test runs with the new table and stored procedure...
*/
exec dbo.newWorkOrder #WorkOrderYear = null,
#WorkOderCounter = null,
#WorkOrderDescription = null;
exec dbo.newWorkOrder #WorkOrderYear = null,
#WorkOderCounter = 3,
#WorkOrderDescription = null;
exec dbo.newWorkOrder #WorkOrderYear = null,
#WorkOderCounter = 0,
#WorkOrderDescription = null;
exec dbo.newWorkOrder #WorkOrderYear = null,
#WorkOderCounter = 0,
#WorkOrderDescription = null;
exec dbo.newWorkOrder #WorkOrderYear = null,
#WorkOderCounter = 0,
#WorkOrderDescription = null;
/*
...reviewing the result of the above.
*/
select *
from dbo.WorkOrders as wo;
Note, that the "next available" work order counter is once given (1) as the maximum + 1 and once (2) increased until it does not violate the unique key constraint on the table anymore. Like this you have two different possibilities to go about it.
There are a number of observations based on your code that you could alter to optimize and guarantee your results.
I am not aware of your Table Structure, but it seems you are using natural keys for your IDs.
Instead, use a surrogate key, such as INT/BIGINT to not only add efficiency in your table joins (no strings required), but potentially add another layer of security in your current design.
Alternatively, normalize the column into the flags they are. For example: OT-001-05 has three elements: OT is a type of work order, 001 is the ID, and 15 is the year. Presently, OT determines the ID which determines the year.
SELECT #aux = id_workorder FROM idaux;
idaux was not described. Is it a single value? If tabular, guarantee the result or it might break in the future.
Even if you add MAX(id_workorder), your result will not work as you think. Since this is a VARCHAR, the greatest value of the leftmost character not tied will return.
#aux, CHARINDEX('-', #aux) + 1,
LEN(#aux) - CHARINDEX('-', #aux) - CHARINDEX('-', REVERSE(#aux)));
This is fine, but overall you could make the code clearer and easier to debug by splitting all three of those elements into their own variable. Your still using your method, but simplified a little (personally, CHARINDEX can be a pain).
SET #aux = #Type -- 'OT'
SET #aux2 = #ID -- The result of your previous code
SET #aux3 = #Year -- your YY from GETDATE()
-- then join
SET #Work_Order = CONCAT(#aux, '-', #aux2, '-', #aux3)
Update:
Currently, your column in idaux has the ID in the MIDDLE of your column. This will produce disastrous results since any comparison of IDs will happen in the middle of the column. This means at best you might get away with PATINDEX but are still performing a table scan on the table. No index (save for FULLTEXT) will be utilized much less optimized.
I should add, if you put the ID element into its own column, you might find using BINARY collations on the column will improve its performance. Note I have not tested attempting a BINARY collation on a mixed column

Search for a common prefix between two strings

I have a Stored Procedure:
ALTER PROCEDURE [dbo].[traxs_Paybook_Data_Validate]
#session_id varchar(30)
#paybook_start_number varchar(30)
#paybook_end_number varchar(30)
AS
UPDATE traxs_temp..__PaybookImport SET
/* BEGIN CHANGE */
prefix = null,
start_number = CAST(#paybook_start_number AS int),
end_number = CAST(#paybook_end_number AS int)
/* END CHANGE */
WHERE
session_id = #session_id
Values were like:
#paybook_start_number = 100
#paybook_end_number = 200
Now paybook numbers can have a prefix, i.e:
#paybook_start_number = ABC100
#paybook_end_number = ABC200
Prefix is not always the same, neither is its length. I need to find the prefix if one, store it into prefix and remove it from paybook numbers before casting them.
Thanks
Try this:
DECLARE #z VARCHAR(32) = 'ukasd10';
SELECT LEFT(#z, PATINDEX('%[0-9]%', #z) - 1) AS Prefix,REPLACE(SUBSTRING(#z, PATINDEX('%[0-9]%', #z), LEN(#z)), ',', '') AS Digits
and likewise use this logic to update the column Prefix....
Thanks
You need a Function to extract Number/Numeric value from your input string and a function to extract Alphabets from the Input string.
Function To Extract Numbers
CREATE FUNCTION dbo.fn_Extract_Numbers
(
#string NVARCHAR(100)
)
RETURNS INT
AS
BEGIN
DECLARE #int_Value INT;
SELECT #int_Value = LEFT(subsrt, PATINDEX('%[^0-9]%', subsrt + 't') - 1)
FROM (
SELECT subsrt = SUBSTRING(#string, pos, LEN(#string))
FROM (
SELECT #string AS string , PATINDEX('%[0-9]%', #string) AS Pos
) d
) t
RETURN #int_Value;
END
Function To Extract Alphabets
CREATE FUNCTION dbo.fn_Extract_Alphabets
(
#string NVARCHAR(100)
)
RETURNS NVARCHAR(100)
AS
BEGIN
DECLARE #Alpha_Value NVARCHAR(100);
SELECT #Alpha_Value = LEFT(subsrt, PATINDEX('%[^a-z]%', subsrt + 't') - 1)
FROM (
SELECT subsrt = SUBSTRING(#string, pos, LEN(#string))
FROM (
SELECT #string AS string , PATINDEX('%[a-z]%', #string) AS Pos
) d
) t
RETURN #Alpha_Value;
END
Now use these functions inside your stored procedure to extract the Alphabet/Prefix bit and the Number bit and store them in the target columns.
Something like....
ALTER PROCEDURE [dbo].[traxs_Paybook_Data_Validate]
#session_id varchar(30)
#paybook_start_number varchar(30)
#paybook_end_number varchar(30)
AS
DECLARE #Start_Num_Prefix VARCHAR(100);
DECLARE #End_Num_Prefix VARCHAR(100);
DECLARE #Start_Num_Numbers INT;
DECLARE #End_Num_Numbers INT;
SELECT #Start_Num_Prefix = dbo.fn_Extract_Alphabets(#paybook_start_number)
SELECT #End_Num_Prefix = dbo.fn_Extract_Alphabets(#paybook_end_number)
SELECT #Start_Num_Numbers = dbo.fn_Extract_Numbers(#paybook_start_number)
SELECT #End_Num_Numbers = dbo.fn_Extract_Numbers(#paybook_end_number)
..... rest of your procedure and so on....

Executing a query on csv data stored in an ntext column

Say that the raw text of CSV exports and an associated timestamps are stored in a database, where one record is equivalent to one export.
Does anyone have a way to execute a query on the CSV file stored in that field without creating a second connection to the database or exporting the data to a file and then reopening it using the csv text driver?
Assume that:
1) you can't write out a physical file onto the server in the solution
2) you can't a second connection to the server w/ OPENROWSET (servers, usernames & passwords change)
3) that it must be a 100% SQL solution - must be able to be run as an SP
4) that you only need to work with one record at time - the solution doesn't need to account for selecting from multiple csv files stored in the DB.
My solution would be to create a UDF that will parse the CSV data into a table variable. Then, in the SP, retrieve the CSV, pass it to the UDF, then run the query against the table variable.
First, create a UDF to return a table from the CSV value (uses CHAR(13) to determine new lines, may need to be altered to work with your data):
CREATE FUNCTION [dbo].[fnParseCSV] (#InputString NVARCHAR(MAX), #Delimiter NCHAR(1) = ',')
RETURNS #tbl TABLE (ID int, Val NVARCHAR(64)) AS
BEGIN
declare #singleLine nvarchar(max)
declare #id int
declare #val varchar(64)
WHILE LEN(#InputString) > 0 BEGIN
IF CHARINDEX(char(13), #InputString) > 0 BEGIN
SELECT #singleLine = SUBSTRING(#InputString, 1, CHARINDEX(char(13), #InputString) - 1)
IF CHARINDEX(#Delimiter, #singleline) > 0 BEGIN
SELECT #id = convert(int, SUBSTRING(#singleline, 1, CHARINDEX(#Delimiter, #singleline) - 1))
SELECT #val = RIGHT(#singleline, LEN(#singleline) - CHARINDEX(#Delimiter, #singleline) )
INSERT INTO #tbl (id, val) values (#id, #val)
END
SELECT #InputString = RIGHT(#InputString, LEN(#InputString) - CHARINDEX(char(13), #InputString) )
END
ELSE
BEGIN
IF CHARINDEX(#Delimiter, #inputString) > 0
BEGIN
SELECT #id = convert(int, SUBSTRING(#inputString, 1, CHARINDEX(#Delimiter, #inputString) - 1))
SELECT #val = RIGHT(#inputString, LEN(#inputString) - CHARINDEX(#Delimiter, #inputString) )
INSERT INTO #tbl (id, val) values (#id, #val)
END
set #inputString = ''
END
END
RETURN
END
Then run the query against that output:
select * from dbo.fnParseCsv('123,val1' + char(13) + '456,val2' + CHAR(13) + '789,val3', ',')
You could set up a series of user-defined functions which could parse through the column. It would likely be slow and wouldn't be robust at all.
As an example though (with no real error checking, etc. and only minimally tested):
IF OBJECT_ID('dbo.Test_CSV_Search') IS NOT NULL
DROP TABLE dbo.Test_CSV_Search
GO
CREATE TABLE dbo.Test_CSV_Search
(
my_id INT IDENTITY NOT NULL,
txt VARCHAR(MAX) NOT NULL,
CONSTRAINT PK_Test_CSV_Search PRIMARY KEY CLUSTERED (my_id)
)
GO
INSERT INTO dbo.Test_CSV_Search (txt) VALUES ('11, 12, 13, 14,15,16
21,22, 23,24, 25,26
31,22,33,34,35,36')
GO
IF OBJECT_ID('dbo.Get_CSV_Row') IS NOT NULL
DROP FUNCTION dbo.Get_CSV_Row
GO
CREATE FUNCTION dbo.Get_CSV_Row
(#my_id INT, #col_num SMALLINT, #search_value VARCHAR(100))
RETURNS #results TABLE (row_num INT, row_txt VARCHAR(MAX))
AS
BEGIN
DECLARE
#csv_txt VARCHAR(MAX),
#full_row VARCHAR(MAX),
#start_pos INT,
#end_pos INT,
#col_txt VARCHAR(100),
#cur_col SMALLINT,
#line_start INT,
#line_end INT,
#row_num INT
SELECT #csv_txt = txt + CHAR(10) FROM dbo.Test_CSV_Search WHERE my_id = #my_id
SELECT
#line_start = 1,
#cur_col = 1,
#start_pos = 1,
#row_num = 1
WHILE (CHARINDEX(CHAR(10), #csv_txt, #line_start) > 0)
BEGIN
SELECT
#line_end = CHARINDEX(CHAR(10), #csv_txt, #line_start),
#end_pos = CHARINDEX(',', #csv_txt, #start_pos)
WHILE (#cur_col < #col_num)
BEGIN
SET #start_pos = #end_pos + 1
SET #end_pos = CHARINDEX(',', #csv_txt, #start_pos)
SET #cur_col = #cur_col + 1
END
IF (RTRIM(LTRIM(SUBSTRING(#csv_txt, #start_pos, #end_pos - #start_pos))) = #search_value)
BEGIN
INSERT INTO #results (row_num, row_txt) VALUES (#row_num, RTRIM(LTRIM(SUBSTRING(#csv_txt, #line_start, #line_end - #line_start))))
END
SELECT
#line_start = #line_end + 1,
#start_pos = #line_end + 1,
#cur_col = 1,
#row_num = #row_num + 1
END
RETURN
END
GO
SELECT * FROM dbo.Get_CSV_Row(1, 1, '11')

Resources