UPDATE dbo.Contact
SET emailMessenger = '63' WHERE personID = #personID
WHILE (SELECT Email FROM Contact WHERE personID = #personID) != #email
BEGIN
IF (SELECT Email FROM Contact WHERE personID = #personID) IS NULL
UPDATE Contact SET Email = #personID WHERE personID = #personID
ELSE IF (SELECT Email FROM Contact WHERE personID = #personID) IS NOT NULL
UPDATE Contact SET SecondaryEmail = email WHERE personID = #personID
UPDATE Contact SET email = #email WHERE personID = #personID
END
What i'm trying to do is add an employees work email to primaryemail. But if they already have a personal email, then I want to first move that to the secondaryemail. But only for the specified employee hence the PersonID.
I've looked at a lot of different examples using CASE and IF-THEN. I think i'm adding too much to the statement. I really thought this Stackoverflow queston would help. I know its going to be the syntax or how it's structured.
Could you can do it with an update avoding trigger
UPDATE dbo.Contact
SET emailMessenger = '63',
dbo.Contact,
SecondaryEmail = CASE when Email is not null then Email else SecondaryEmail end ,
Email = CASE when email is null then 'studentemail.com'
when email <>'studentemail.com' then 'studentemail.com' else email end
where personID = '18403';
If I'm following your logic correctly, the following should do the trick.
I first set up a testing table with all possible problem permutations (email present in one, both, or neither column). If your data doesn't look something like this, my solution may not apply.
drop table Contact
CREATE TABLE Contact
(
PersonId int not null
,Email varchar(100) null
,SecondaryEmail varchar(100) null
)
INSERT Contact values
(1, null, null)
,(2, 'OnlyFirst', null)
,(3, null, 'OnlySecond')
,(4, 'FirstEmail', 'SecondEmail')
And the following to set the change to make, show contents before, make the change, then show contents after.
DECLARE
#PersonId int
,#Email varchar(100) = 'NewEmail#foo.com'
SET #PersonId = 4
-- Before
SELECT * from Contact
-- Modify
UPDATE Contact
set
Email = #Email
,SecondaryEmail = case when Email is not null then Email else SecondaryEmail end
where PersonID = #PersonID
-- After
SELECT * from Contact
Net result: #Email is always set in column Email, and #SecondaryEmail is set to the contents of Email only if Email was not null
Related
I'm trying to migrate a Tomcat app from using Postgres 9.5 to SQL Server 2016 and I've got a problem statement I can't seem to duplicate.
It's basically an upsert but one of the complications is the request supplies arguments to do the update, but when there is conflict I need to use some of the existing values from conflicting rows to insert/update.
The primary keys in the table can sometimes cause a conflict, which requires updating rows and deleting the old ones.
The table schema in MS SQL looks like:
CREATE TABLE [dbo].[signup](
[site_key] [varchar](32) NOT NULL,
[list_id] [bigint] NOT NULL,
[email_address] [varchar](256) NOT NULL,
[customer_id] [bigint] NULL,
[attribute1] [varchar](64) NULL,
[date1] [datetime] NOT NULL,
[date2] [datetime] NULL,
CONSTRAINT [pk_signup] PRIMARY KEY CLUSTERED
(
[site_key] ASC,
[list_id] ASC,
[email_address] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
The old Postgres SQL looked like this:
WITH updated_rows AS (
INSERT INTO signup
(site_key, list_id, email_address, customer_id, attribute1, date1, date2)
SELECT site_key, list_id, :emailAddress, customer_id, attribute1, date1, date2
FROM signup WHERE customer_id = :customerId and email_address <> :emailAddress
ON CONFLICT (site_key, list_id, email_address) DO UPDATE SET customer_id = excluded.customer_id
RETURNING site_key, customer_id, email_address, list_id
)
DELETE FROM signup AS signup_delete USING updated_rows
WHERE
signup_delete.site_key = updated_rows.site_key
AND signup_delete.customer_id = updated_rows.customer_id
AND signup_delete.list_id = updated_rows.list_id
AND signup_delete.email_address <> :emailAddress;
Two arguments are supplied, customer id and email address, shown here as Spring NamedParameterJdbcTemplate values :customerId and :emailAddress
It's trying to change the email address of the customer id to be the supplied one, but sometimes the supplied email address already exists in the primary key constraint.
In which case it needs to change the existing customer id to be supplied one, and remove the rows with that don't match the new email address.
I also need to try and maintain isolation so that nothing can change the data whilst I'm updating.
I'm trying to do it with a MERGE statement but I can't seem to get it to work, it's complaining I cant use values that aren't in the clause scope, but I think I've probably got other issues here too.
This is what I had so far. It doesn't even address the deleting part - only the upserting, but I can't even get this part to work. I was planning to use the OUTPUT from this as input to something to delete the rows similar to the postgres version.
WITH source AS (
SELECT cs.[site_key] as existing_site_key,
cs.list_id as existing_list_id,
cs.email_address as existing_email,
cs.customer_id as existing_customer_id,
cs.attribute1 as existing_attribute1,
cs.date1 as existing_date1,
cs.date2 as existing_date2,
cs2.email_address as conflicting_email,
cs2.customer_id AS conflicting_customer_id
FROM [dbo].[signup] cs
LEFT JOIN [dbo].[signup] cs2 ON cs2.email_address = :emailAddress
AND cs.site_key = cs2.site_key
AND cs.list_id = cs2.list_id
WHERE cs.customer_id = :customerId
)
MERGE signup WITH (HOLDLOCK) AS target
USING source
ON ( source.conflicting_customer_id is not null )
WHEN MATCHED AND source.existing_site_key = target.site_key AND source.existing_list_id = target.list_id AND source.conflicting_email = target.email_address THEN UPDATE
SET customer_id = :customerId
WHEN NOT MATCHED BY target AND source.existing_site_key = target.site_key AND source.existing_list_id = target.list_id AND source.conflicting_customer_id = :customerId THEN INSERT
(site_key, list_id, email_address, customer_id, attribute1, date1, date2) VALUES
(source.existing_site_key, source.existing_list_id, :emailAddress, source.customer_id, source.existing_attribute1, source.existing_date1, source.existing_date2)
Thanks,
mikee
I have a field in my database table called ADDRESSFORMAT
1,The Lodge
Street
Town
Postcode
Where the contents are separated by a CHAR (13) and CHAR (10)
How would I go about creating fields in a query that would only pull back either the first line, second line...and so on?
The following is an in-line approach.
The Cross Apply B generates a "clean string". It will eliminate any number of repeating CRLFs and create a pipe delimited string to be processed by Cross Appy C.
I should note that this method of eliminating repeating strings was demonstrated by Gordon Linoff several weeks back. Sorry I can't find the original post.
Example
Declare #YourTable table (ID int,ADDRESSFORMAT varchar(max))
Insert Into #YourTable values
(1,'The Lodge
Street
Town
Postcode')
Select A.ID
,C.*
From #YourTable A
Cross Apply (
Select CleanString = replace(replace(replace(replace(replace(ADDRESSFORMAT,char(13),'|'),char(10),'|'),'|','><'),'<>',''),'><','|')
) B
Cross Apply (
Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(max)')))
,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(max)')))
From (Select Cast('<x>' + replace((Select replace(B.CleanString,'|','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A
) C
Returns
ID Pos1 Pos2 Pos3 Pos4 Pos5 Pos6 Pos7 Pos8 Pos9
1 The Lodge Street Town Postcode NULL NULL NULL NULL NULL
im working on procedure which should transfer number of items (value #p_count) from old store to new store
SET #countOnOldStore = (SELECT "count" FROM ProductStore WHERE StoreId = #p_oldStoreId AND ProductId = #p_productID)
SET #countOnNewStore = (SELECT "count" FROM ProductStore WHERE StoreId = #p_newStoreID AND ProductId = #p_productID)
SET #ShiftedCount = #countOnOldStore - #p_count
SET #newStoreAfterShift = #countOnNewStore + #p_count
IF #ShiftedCount > 0
BEGIN
DELETE FROM ProductStore WHERE storeId = #p_oldStoreId and productID = #p_productID
INSERT INTO ProductStore (storeId,productId,"count") VALUES (#p_oldStoreId,#p_productID,#ShiftedCount)
DELETE FROM ProductStore WHERE storeId = #p_newStoreID and productID = #p_productID
INSERT INTO ProductStore (storeId,productId,"count") VALUES (#p_newStoreID,#p_productID,#newStoreAfterShift)
END
ELSE
PRINT 'ERROR'
well ... second insert is not working. I cant figure it out. It says
Cannot insert the value NULL into column 'count', table 'dbo.ProductStore'; column does not allow nulls. INSERT fails.
Can anyone see problem and explain it to me ? Its school project
It looks like your entire query should just be:
UPDATE ProductStore
SET [count] = [count] + CASE
WHEN storeId = #p_NewStoreID THEN #p_Count
ELSE -#p_Count END
WHERE
productID = #p_ProductID and
storeID in (#p_NewStoreID,#p_OldStoreID)
If either value in the following is NULL, the total will be NULL:
SET #newStoreAfterShift = #countOnNewStore + #p_count
Check both values (#countOnNewStore, #p_count) for NULL.
Looks like you are not assigning any value to #p_count, so it is NULL and so are #ShiftedCount and #newStoreAfterShift.
I want to have one trigger to handle updates and inserts. Most of the sql actions in the trigger are for both. The only exception is the fields I'm using to record date and username for an insert and an update. This is what I have, but the updates of the fields used to track update and insert are not firing right. If I insert a new record, I get CreatedBy, CreatedOn, LastEditedBy, LastEditedOn populated, with LastEditedOn as 1 second after CreatedOn (which I dont want to happen). When I update the record, only the LastEditedBy & LastEditedOn changes (which is correct). I'm including my full trigger for reference:
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
-- =================================================================================
-- Author: Paul J. Scipione
-- Create date: 2/15/2012
-- Update date: 6/5/2012
-- Description: To concatenate several fields into a set formatted UnitDescription,
-- to total Span & Loop footages, to set appropriate AcctCode, & track
-- user inserts
-- =================================================================================
IF OBJECT_ID('ProcessCable', 'TR') IS NOT NULL
DROP TRIGGER ProcessCable
GO
CREATE TRIGGER ProcessCable
ON Cable
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
-- IF TRIGGER_NESTLEVEL() > 1 RETURN
IF ((SELECT TRIGGER_NESTLEVEL()) > 1 )
RETURN
ELSE
BEGIN
-- record user and date of insert or update
IF EXISTS (SELECT * FROM DELETED)
UPDATE Cable SET LastEditedOn = getdate(), LastEditedBy = REPLACE(user_name(), 'GRTINET\', '')
ELSE IF NOT EXISTS (SELECT * FROM DELETED)
UPDATE Cable SET CreatedOn = getdate(), CreatedBy = REPLACE(user_name(), 'GRTINET\', '')
-- reset Suffix if applicable
UPDATE Cable SET Suffix = NULL WHERE Suffix = 'n/a'
-- create UnitDescription value
UPDATE Cable SET UnitDescription =
isnull (Type, '') +
isnull (CONVERT (NVARCHAR (10), Size), '') +
'-' +
isnull (CONVERT (NVARCHAR (10), Gauge), '') +
CASE
WHEN ExtraTrench IS NOT NULL AND ExtraTrench > 0 THEN
CASE
WHEN Suffix IS NULL THEN 'TE' + '(' + CONVERT (NVARCHAR (10), ExtraTrench) + ')'
ELSE 'TE' + '(' + CONVERT (NVARCHAR (10), ExtraTrench) + ')' + Suffix
END
ELSE isnull (Suffix, '')
END
-- convert any accidental negative numbers entered
UPDATE Cable SET Length = ABS(Length)
-- sum Length with LoopFootage into TotalFootage
UPDATE Cable SET TotalFootage = isnull(Length, 0) + isnull(LoopFootage, 0)
-- set proper AcctCode based on Type
UPDATE Cable SET AcctCode =
CASE
WHEN Type IN ('SEA', 'CW', 'CJ') THEN '32.2421.2'
WHEN Type IN ('BFC', 'BJ', 'SEB') THEN '32.2423.2'
WHEN Type IN ('TIP','UF') THEN '32.2422.2'
WHEN Type = 'unknown' OR Type IS NULL THEN 'unknown'
END
WHERE AcctCode IS NULL OR AcctCode = ' '
END
END
GO
A few things jump out at me when I look at your trigger:
You are doing several additional updates rather than a single update (performance-wise, a single update would be better).
Your update statements are unconstrained (there is no JOIN to the inserted/deleted tables to limit the number of records that you perform these additional updates on).
Most of this logic feels like it should be in the application layer rather than in the database; Or, perhaps in some cases implemented differently.
Some quick examples:
Suffix of "n/a" should be removed before inserted.
Cable length absolute value should be done before inserted (with a CHECK CONSTRAINT to verify that bad data cannot be inserted).
TotalFootage should be a computed column so it is always correct.
The Type/AcctCode relationship seems like it should be a column value in a foreign key reference.
But ultimately, I think the reason you are seeing the unexpected dates is because of the unconstrained updates. Without addressing any of the other concerns I brought up above, the statement that sets the audit fields should be more like this:
UPDATE Cable SET LastEditedOn = getdate(), LastEditedBy = REPLACE(user_name(), 'GRTINET\', '')
FROM Cable
JOIN deleted on Cable.PrimaryKeyColumn = deleted.PrimaryKeyColumn
UPDATE Cable SET CreatedOn = getdate(), CreatedBy = REPLACE(user_name(), 'GRTINET\', '')
FROM Cable
JOIN inserted on Cable.PrimaryKeyColumn = inserted.PrimaryKeyColumn
LEFT JOIN deleted on Cable.PrimaryKeyColumn = deleted.PrimaryKeyColumn
WHERE deleted.PrimaryKeyColumn IS NULL
A location table record has two address id's - mailing and business addressID that refer to an address table.
Thus, the address table will contain up to two records for a given addressID.
Given a location ID, I need an sproc to return all tbl_Location fields, and all tbl_Address fields in one resultset:
LocationID INT,
ClientID INT,
LocationName NVARCHAR(50),
LocationDescription NVARCHAR(50),
MailingAddressID INT,
BillingAddressID INT,
MAddress1 NVARCHAR(255),
MAddress2 NVARCHAR(255),
MCity NVARCHAR(50),
MState NVARCHAR(50),
MZip NVARCHAR(10),
MCountry CHAR(3),
BAddress1 NVARCHAR(255),
BAddress2 NVARCHAR(255),
BCity NVARCHAR(50),
BState NVARCHAR(50),
BZip NVARCHAR(10),
BCountry CHAR(3)
I've started by creating a temp table with the required fields, but am a bit stuck on how to accomplish this.
I could do sub-selects for each of the required address fields, but seems a bit messy.
I've already got a table-valued-function that accepts an address ID, and returns all fields for that ID, but not sure how to integrate it into my required result.
Off hand, it looks like 3 selects to create this table - 1: Location, 2: Mailing address, 3: Billing address.
What I'd like to do is just create a view and use that.
Any assistance would be helpful.
Thanks.
something along the lines of the following would work:
select L.*,
a1.Address1 as MAddress1, a1.Address2 as MAddress2,
a2.Address1 as BAddress1, a2.Address2 as BAddress2
from location L
inner join Address a1 on (a1.AddressId = L.MailingAddressId)
inner join Address a2 on (a2.AddressId = L.BillingAddressId)
I didn't put in all of the fields, but you get the idea.
Note that if either of the address ids could be null, the you might use a left join instead.
If I understand your question correctly you want something like:
SELECT
L.*,
MAddress1 = M.Address1,
MAddress2 = M.Address2,
MCity = M.City,
MState = M.State,
MZip = M.Zip,
MCountry = M.Country
BAddress1 = B.Address1,
BAddress2 = B.Address2,
BCity = B.City,
BState = B.State,
BZip = B.Zip,
BCountry = B.Country
FROM
tbl_Location L
INNER JOIN tbl_Address M
ON L.MailingAddressID = M.MailingAddressID
INNER JOIN tbl_Address B
ON L.BillingAddressID = B.BillingAddressID
WHERE
L.LocationID = #LocationID