SQL UPDATE (but only if its going to be unique) - sql

I need to write an SQL string that will UPDATE a value into a table, but only if that value is unique. But it should allow blank values ('') to be duplicates.
UPDATE [knw].[dbo].[Nop_ProductVariant]
SET barcode = (CASE WHEN (SELECT count(*) FROM [knw].[dbo].[Nop_ProductVariant] WHERE barcode = '" + item + "') = 0 THEN '" + item + "' ELSE '' END)
WHERE ProductVariantId='" + s + "'"
This functions seems to wipe all data and make them blank (''). I think this is because if the current value that I am changing is the same value, (i.e. changing 'purple' to 'purple') it thinks it would be a duplicate and puts a blank one in.
Help!

Could you modify the SELECT inside your CASE slightly to exclude the current ProductVariantId?
SELECT count(*)
FROM [knw].[dbo].[Nop_ProductVariant]
WHERE barcode = '" + item + "'
AND ProductVariantId <> '" + s + "'"

I guess you want something like this:
UPDATE [knw].[dbo].[Nop_ProductVariant]
SET barcode = #NewValue
WHERE NOT EXISTS
(SELECT 1
FROM [knw].[dbo].[Nop_ProductVariant]
WHERE barcode = #NewValue)
AND ... ? ;

Why not simply run it in two queries:
Query 1:
Update [knw].[dbo].[Nop_ProductVariant]
Set barcode = '"' + [knw].[dbo].[Nop_ProductVariant].item + '"'
Where ProductVariantId='"' + s + '"'
And Exists (
Select 1
From [knw].[dbo].[Nop_ProductVariant] As PV1
Where PV1.barcode = '"' + [knw].[dbo].[Nop_ProductVariant].[item] + '"'
)
Query 2:
Update [knw].[dbo].[Nop_ProductVariant]
Set barcode = ''
Where ProductVariantId='"' + s + '"'
And Not Exists (
Select 1
From [knw].[dbo].[Nop_ProductVariant] As PV1
Where PV1.barcode = '"' + [knw].[dbo].[Nop_ProductVariant].[item] + '"'
)

Normally, you should use a UNIQUE index for such situations.
In SQL Server 2008:
CREATE UNIQUE INDEX ux_productvariant_barcode ON (barcode) WHERE barcode <> ''
If you are not able to create such an index, use this:
WITH q AS
(
SELECT *
FROM [knw].[dbo].[Nop_ProductVariant]
WHERE ProductVariantId = #s
)
UPDATE q
SET barcode = #item
WHERE #item NOT IN
(
SELECT barcode
FROM q
WHERE barcode <> ''
)

This shows all unique non-empty barcodes along with their first-entry IDs:
SELECT barcode, MIN(ProductVariantId) AS ProductVariantId
FROM [knw].[dbo].[Nop_ProductVariant]
WHERE barcode <> ''
GROUP BY barcode
Accordingly, this shows all their duplicates (full rows this time, not including the IDs from the previous selection):
SELECT npv.*
FROM [knw].[dbo].[Nop_ProductVariant] AS npv
LEFT JOIN (
SELECT MIN(ProductVariantId) AS ProductVariantId
FROM [knw].[dbo].[Nop_ProductVariant]
WHERE barcode <> ''
GROUP BY barcode
) npv_u ON npv.ProductVariantId = npv_u.ProductVariantId
WHERE npv.barcode <> '' AND npv_u.ProductVariantId IS NULL
Personally I would first check out those lists. If they are alright, the following script can be used to blank the duplicates:
UPDATE npv
SET barcode = ''
FROM [knw].[dbo].[Nop_ProductVariant] AS npv
LEFT JOIN (
SELECT MIN(ProductVariantId) AS ProductVariantId
FROM [knw].[dbo].[Nop_ProductVariant]
WHERE barcode <> ''
GROUP BY barcode
) npv_u ON npv.ProductVariantId = npv_u.ProductVariantId
WHERE npv.barcode <> '' AND npv_u.ProductVariantId IS NULL

Related

SQL - concatenate strings in variable while loop with +=

I can't concatenate in this example bellow!
When I loop I get my 2 correct results.
When I concatenate #MaterialCompositionEn += ', ' it works fine
When I try to concatenate #MaterialCompositionEn += same query to get the 2nd row, I have a null!
DECLARE #MaterialCompositionId int = 475;
DECLARE #MaterialCompositionKey nvarchar(50) = '202071512324138';
DECLARE #Records nvarchar(250);
DECLARE #RecordProceed int;
DECLARE #MaterialCompositionEn nvarchar(500);
SET #Records = (SELECT STRING_AGG(Id, ',') FROM MaterialCompositions mc WHERE mc.MaterialCompositionId = #MaterialCompositionId)
WHILE len(#Records) > 0
BEGIN
SET #RecordProceed = CAST(LEFT(#Records,4) AS int)
if #RecordProceed > 0
BEGIN
SET #Records = REPLACE(#Records,substring(#Records, 1, 4),'')
END
if len(#Records) > 4
BEGIN
SET #Records = REPLACE(#Records,substring(#Records, 1, 1),'')
END
if len(#MaterialCompositionEn) > 0
BEGIN
SET #MaterialCompositionEn += ', '
END
PRINT 'MaterialCompositionEn1: ' + #MaterialCompositionEn
SET #MaterialCompositionEn =
(SELECT COALESCE (CAST(MaterialProportion AS nvarchar) + '% ', '') +
(SELECT mp.MaterialPrimaryEn +
COALESCE(
(SELECT ' (' + ms.MaterialSecondaryEn + ')' AS MS1 FROM dbo.MaterialSecondaries AS ms WHERE ms.Id = mc.MaterialSecondaryId)
, '')
FROM dbo.MaterialPrimaries AS mp WHERE mp.Id = mc.MaterialPrimaryId)
FROM MaterialCompositions mc WHERE mc.Id = #RecordProceed
)
PRINT 'MaterialCompositionEn2: ' + #MaterialCompositionEn
END
Result:
MaterialCompositionEn2: 20% Cashmere
MaterialCompositionEn1: 20% Cashmere,
MaterialCompositionEn2: 80% Wool
Now when I change to:
SET #MaterialCompositionEn +=
(SELECT COALESCE......
I am expecting 20% Cashmere, 80% Wool
instead my 3 prints are NULL
I tried to CAST but won't help.
Any idea?
Thanks in advance
I'm guessing there is a much simpler way to do what you want. However, I think the problem is that you need to initialize the string. So at the top of the code block put:
SET #MaterialCompositionEn = '';
SET #MaterialCompositionEn =
SELECT
CONCAT(
mp.MaterialPrimaryEn, ' ', MaterialProportion, '% ', --always show primary
', ' + ms.MaterialSecondaryEn + CONCAT(100 - MaterialProportion, '%') --sometimes show secondary
)
FROM
MaterialCompositions mc
INNER JOIN dbo.MaterialPrimaries mp ON mp.Id = mc.MaterialPrimaryId
LEFT JOIN dbo.MaterialSecondaries ms ON ms.Id = mc.MaterialSecondaryId
WHERE mc.Id = #RecordProceed
)
Something like this might be neater.. Note that I'm not clear where MaterialProportion comes from (I suspect MaterialCompositions), so this perhaps isn't a solution, just a note as to how you might use CONCAT/avoid having boatloads of nested selects. The use of INNER/OUTER join links compositions and definitely a primary, possibly a secondary material. If the secondary material is null then the aim is to hide it with a mix and match of CONCAT and +
CONCAT treats nulls as empty strings, where as + causes the whole expression to become null. This can be useful to mix and match e.g. in something like ', ' + ms.MaterialSecondaryEn + CONCAT(100 - MaterialProportion, '%'):
the CONCAT(100 - MaterialProportion, '%') would be 20% (if the primary material was 80%) but
the ', ' + ms.MaterialSecondaryEn + CONCAT(...) as a whole is NULL if MaterialSecondaryEn IS NULL from a left join fail to match, so where there is only a primary material, a the string describing the secondary should disappear entirely as NULL, which the outer CONCAT handles as an empty string

Disregard certain lines if false

I wrote this query and it is designed to send an email to a customer with info in a database. I would like it to do a check for any values that return as 0 and not send them in the #message. I imagine I'll need an if statement but I haven't been able to figure out how to make it work. Any help is appreciated.
Query:
select #AssignedCount = (select COUNT(*)
FROM event
where status_name = 'ASSIGNED' AND primary_unitid = NULL
OR status_name = 'ASSIGNED' AND primary_unitid = '')
select #UnitResource = (select COUNT (*)
from unit_resources
where unit_seqnum NOT IN (select unit_seqnum from unit))
select #UnitEmployee = (select COUNT (*)
from unit_employees
where unit_seqnum NOT IN (select unit_seqnum from unit))
select #LoggedOff = (select COUNT(*)
from unit
where status_name = 'LOGGED_OFF')
select #Duplicates = (SELECT ISNULL(
(select COUNT(*)
from unit
group by unitid
having COUNT(*) > 1), 0))
select #message =
'Status Report' +
' Events in assigned status with no primary unit: '
+ REPLACE((str(#AssignedCount)),' ','') +
' Un-linked unit resource table rows: '
+ REPLACE((str(#UnitResource)),' ','') +
' Un-linked Unit resource table rows: '
+ REPLACE((str(#UnitEmployee)),' ','') +
' Units with a status of Logged Off: '
+ REPLACE((str(#LoggedOff)),' ','') +
' Duplicate Units: '
+ REPLACE((str(#Duplicates)),' ','')`
You will have to use if statements to dynamically build the string:
-- setting to blank; otherwise, the string would be null
set #message = ''
-- dynamically building #message with if statements
if #AssignedCount > 0
set #message = 'Status Report' +
' Events in assigned status with no primary unit: '
+ REPLACE((str(#AssignedCount)),' ','')
if #UnitResource > 0
set #message = #message + ' Un-linked unit resource table rows: '
+ REPLACE((str(#UnitResource)),' ','')
if #UnitEmployee > 0
set #message = #message + ' Un-linked Unit resource table rows: '
+ REPLACE((str(#UnitEmployee)),' ','')
if #LoggedOff > 0
set #message = #message + ' Units with a status of Logged Off: '
+ REPLACE((str(#LoggedOff)),' ','')
if #Duplicates > 0
set #message = #message + ' Duplicate Units: '
+ REPLACE((str(#Duplicates)),' ','')
-- removing leading space from #message if AssignedCount is 0
set #message = ltrim(#message)
Personally, actually, rather than a bunch of IF statements, I'd likely go for something like this and do it all in one go:
SET #message = CONCAT('Status Report',
(SELECT CONCAT(' Events in assigned status with no primary unit: ',COUNT(*))
FROM event
WHERE status_name = 'ASSIGNED'
AND primary_unitid = NULL
OR status_name = 'ASSIGNED'
AND primary_unitid = ''
HAVING COUNT(*) > 0),
((SELECT CONCAT(' Un-linked unit resource table rows: ',COUNT (*))
FROM unit_resources ur
WHERE NOT EXISTS (SELECT 1 --I changed this from a NOT IN to an EXISTS, as NOT in have behave oddly with NULLs
FROM unit u
WHERE u.unit_seqnum = ur.seqnum)
HAVING COUNT(*) > 0))); --You get the idea now
I haven't done the whole lot here, however, the HAVING COUNT(*) > 0 means that no rows (including 0) will be returned when there are no relevant rows. This means that information won't be concatenated to the value of #message.

some weird signs appearing on SQL SERVER Query output

i have written a SELECT Query on SQL SERVER 2014 . I have got the desired output . but an apostrophe symbol(') appearing on 'TaskAction' field data(at the end of each data). here it is my script:
SELECT
WOtask.PK,
WOPK,
TaskNo,
TaskAction =
CASE
WHEN WOTask.AssetPK IS NOT NULL THEN '<b>' + Asset.AssetName + ' [' + Asset.AssetID + ']</b> ' + CASE
WHEN Asset.Vicinity IS NOT NULL AND
Asset.Vicinity <> '''' THEN RTRIM(Asset.Vicinity) + ': '
ELSE ''''
END + WOtask.TaskAction + CASE
WHEN CONVERT(varchar, ValueLow) IS NOT NULL AND
CONVERT(varchar, ValueHi) IS NOT NULL AND
Spec = 1 THEN ' (Range: '' + CONVERT(VARCHAR,CONVERT(FLOAT,ISNULL(ValueLow,0))) + '' - '' + CONVERT(VARCHAR,CONVERT(FLOAT,ISNULL(ValueHi,0))) + )'
ELSE ''''
END
ELSE WOtask.TaskAction + CASE
WHEN CONVERT(varchar, ValueLow) IS NOT NULL AND
CONVERT(varchar, ValueHi) IS NOT NULL AND
Spec = 1 THEN ' (Range: '' + CONVERT(VARCHAR,CONVERT(FLOAT,ISNULL(ValueLow,0))) + '' - '' + CONVERT(VARCHAR,CONVERT(FLOAT,ISNULL(ValueHi,0))) + )'
ELSE ''''
END
END,
Rate,
Measurement,
Initials,
Fail,
Complete,
Header,
LineStyle,
WOtask.Comments,
WOtask.NotApplicable,
WOTask.Photo1,
WOTask.Photo2
FROM WOtask WITH (NOLOCK)
LEFT OUTER JOIN Asset WITH (NOLOCK)
ON Asset.AssetPK = WOTask.AssetPK
LEFT OUTER JOIN AssetSpecification
ON AssetSpecification.PK = WOTask.AssetSpecificationPK
WHERE (WOPK IN (SELECT
WOPK
FROM WO WITH (NOLOCK)
LEFT OUTER JOIN Asset WITH (NOLOCK)
ON Asset.AssetPK = WO.AssetPK
LEFT OUTER JOIN AssetHierarchy WITH (NOLOCK)
ON AssetHierarchy.AssetPK = WO.AssetPK
WHERE WO.WOPK = 10109)
)
ORDER BY WOPK, TaskNo
now please check my output and error
please help to solve this issue. Thanks in Advance.
As noted in comments, use ELSE '' instead of ELSE ''''. The reason is that within a pair of single quotes, then '' tells SQL to escape a single quote into your output.
For instance, to show the output User's, you would need SELECT 'User''s'.

How to remove first and last charcter if character is ',' of name in Sql query

I combine two column value with ',' using below query.
SELECT
RTRIM(LTRIM(REPLACE(
IsNull(tbl1 .Reason,'') + ',' + IsNull(tbl2.OtherReason,''),',' ,'')))
FROM tbl1
LEFT JOIN tbl2 ON tbl2.OtherReasonId = tbl1.ReasonId
Now issue is that it remove all ',' using above query and I want to just remove last and first ','.
I have combine two column. Now
if tbl1 .Reason is null then it display output as " ,tbl2.OtherReason " and if tbl2.OtherReason is null the output is "tbl1 .Reason,"
Before above query, I also try with below query:
SELECT
IsNull(tbl1.Reason,'') + ',' + IsNull(tbl2.OtherReason,'')
FROM tbl1
LEFT JOIN tbl2 ON tbl2.OtherReasonId = tbl1.ReasonId
Thanks,
HItesh
You can use a case in the middle to check if the values are null or not.
declare #a nvarchar(5)
,#b nvarchar(5)
set #a = 'abc'
set #b = null--'123'
select isnull(#a, '') + case when #a is not null and #b is not null then ',' else '' end + isnull(#b, '')
Try this:
SELECT
CASE
WHEN tbl1.Reason IS NULL
THEN
CASE
WHEN tbl2.Reason IS NULL
THEN ''
ELSE LTRIM(RTRIM(tb2.Reason))
END
ELSE tbl1.Reason +
CASE
WHEN tbl2.Reason IS NULL
THEN ''
ELSE ',' + LTRIM(RTRIM(tb2.Reason))
END
END
FROM tbl1
LEFT JOIN tbl2 ON tbl2.OtherReasonId = tbl1.ReasonId
You can concatenate a comma to the first value in case that second value is not null :
SELECT
IsNull(tbl1.Reason + case when tbl2.OtherReason is null then '' else ',' end, '') +
IsNull(tbl2.OtherReason,'')
FROM tbl1
LEFT JOIN tbl2 ON tbl2.OtherReasonId = tbl1.ReasonId

SQL query returns multiple rows when trying to find specific value

I have 2 tables. One is called "Tasks" and the other one is called "TaskDescription"
in my "Task" the setup looks like this:
"taskID(primary)","FileID","TaskTypeID" and a bunch of other columns irrelevant.
Then in my "TaskDescription", the setup looks like:
"TaskTypeID", "TaskTypeDesc"
so for example if TaskTypeID is 1 , then the description would be"admin"
or if TaskTypeID is 2, then TaskTypeDesc would be "Employee" etc.
The two tables have a relationship on the primary/foreign key "TaskTypeID".
What I am trying to do is get a task id, and the TaskDesc where the FileID matches the #fileID(which I pass in as a param). However in my query I get multiple rows returned instead of a single row when trying to obtain the description.
this is my query:
SELECT taskid,
( 'Task ID: '
+ Cast(cf.taskid AS NVARCHAR(15)) + ' - '
+ Cast((SELECT DISTINCT td.tasktypedesc FROM casefiletaskdescriptions
td JOIN
casefiletasks cft ON td.tasktypeid=cft.tasktypeid WHERE cft.taskid =
1841 )AS
NVARCHAR(100))
+ ' - Investigator : ' + ( Cast(i.fname AS NVARCHAR(20)) + ' '
+ Cast(i.lname AS NVARCHAR(20)) ) ) AS
'Display'
FROM casefiletasks [cf]
JOIN investigators i
ON CF.taskasgnto = i.investigatorid
WHERE cf.fileid = 2011630988
AND cf.concluded = 0
AND cf.progressflag != 'Conclude'
I am trying to get the output to look like "Task ID: 1234 - Admin - Investigator : John Doe". However I am having trouble on this part:
CAST((select DISTINCT td.TaskTypeDesc from CaseFileTaskDescriptions td
JOIN CaseFileTasks cft ON td.TaskTypeID=cft.TaskTypeID
where cft.TaskID =1841 )as nvarchar(100))
This seems to work but the problem is I have to hard code the value "1841" to make it work. Is there a way to assign a "taskID" variable with the values being returned from the TaskID select query, or will it not work since I think sql runs everything at once instead of line by line.
EDIT-this is in Microsoft SQL Server Management Studio 2008
You can dynamically reference a column that exists in your FROM set. In this case, it would be any column from casefiletasks or investigators. You would replace 1841 with the table.column reference.
Update
Replacing your static integer with the column reference, your query would look like:
SELECT taskid,
( 'Task ID: '
+ Cast(cf.taskid AS NVARCHAR(15)) + ' - '
+ Cast((SELECT DISTINCT td.tasktypedesc FROM casefiletaskdescriptions
td JOIN
casefiletasks cft ON td.tasktypeid=cft.tasktypeid WHERE cft.taskid =
cf.taskid )AS
NVARCHAR(100))
+ ' - Investigator : ' + ( Cast(i.fname AS NVARCHAR(20)) + ' '
+ Cast(i.lname AS NVARCHAR(20)) ) ) AS
'Display'
FROM casefiletasks [cf]
JOIN investigators i
ON CF.taskasgnto = i.investigatorid
WHERE cf.fileid = 2011630988
AND cf.concluded = 0
AND cf.progressflag != 'Conclude'
Would this work as your inner query?
SELECT DISTINCT td.TaskTypeDesc FROM CaseFileTaskDescriptions td
JOIN CaseFileTasks cft ON td.TaskTypeID = cft.TaskTypeID
WHERE cft.TaskID = cf.TaskID
Why not just do another join instead of a subquery?
SELECT taskid,
( 'Task ID: '
+ Cast(cf.taskid AS NVARCHAR(15)) + ' - '
+ Cast(td.tasktypedesc AS NVARCHAR(100))
+ ' - Investigator : ' + ( Cast(i.fname AS NVARCHAR(20)) + ' '
+ Cast(i.lname AS NVARCHAR(20)) ) ) AS
'Display'
FROM casefiletasks [cf]
JOIN investigators i
ON CF.taskasgnto = i.investigatorid
JOIN casefiletaskdescriptions td
ON td.tasktypeid = cf.tasktypeid
WHERE cf.fileid = 2011630988
AND cf.concluded = 0
AND cf.progressflag != 'Conclude'