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.
Related
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
I'm working on an issue with a stored procedure. I have the following:
SELECT #message = 'ID' + CAST(CASE WHEN #StoreID = 0 THEN 'BK'
WHEN #StoreID = 1 THEN 'MK'
END AS VARCHAR (50)) + char(13)
This sends an e-mail that says: ID: 'MK' or ID:'BK'.
Currently i'm hard-coding the case statement, but I need to pull the strings 'BK' 'MK' from a different table all together.
The #StoreID is from the Store_Orders table. The names are from the Store table.
One way to get this I tried was doing this:
SELECT #message = 'ID' + StoreName from db.Store where StoreName = StoreID and StoreID = #StoreID
When I execute the code, it finds the correct store, but says that
Conversion failed when converting the varchar value 'MK' to data type
int.
But I don't want to convert 'MK' I want to display 'MK' by finding it via the #StoreID.
Are you looking for something like this:
SELECT CONCAT('ID: ' , CASE WHEN s.StoreID = 0
THEN (select '(''' + colname + ''')'
from othertable ot where ot.colname = s.colname)
WHEN s.StoreID = 1
THEN (select '(''' + colname + ''')'
from othertable ot where ot.colname = s.colname)
END) AS 'Message'
FROM Store_Orders s
SELECT
CASE
WHEN Employees.first_name IS NULL
OR Employees.first_name = 'x' THEN Employees.last_name
WHEN Employees.credentials IS NULL THEN Employees.last_name + ', ' + Employees.first_name
ELSE Employees.last_name + ', ' + Employees.first_name + ' - ' + Employees.credentials
END,
Employees.num3,
Employees.address1 + ' ' + Employees.city + ', ' + Employees.state + ' ' + Employees.zip,
Employees.work_phone,
CASE
WHEN Clients.age <= 18 THEN 'Youth'
ELSE 'Adult'
END,
Clients.client_id,
Clients.last_name + ', ' + Clients.first_name,
ClientVisit.cptcode,
ClientVisit.visittype,
ClientVisit.rev_timeout,
ClientVisit.timein,
ClientVisit.duration,
SUM(CASE
WHEN ClientVisit.cptcode = 90791 THEN 200
WHEN ClientVisit.comb_units = 1 THEN 85.67
ELSE ClientVisit.comb_units * 21.4175
END),
DATEDIFF(d, ClientVisit.rev_timeout, ClientVisit.signature_datetime)
FROM dbo.ClientVisit
INNER JOIN dbo.Employees
ON (
ClientVisit.by_emp_id = Employees.emp_id
)
INNER JOIN dbo.Programs
ON (
ClientVisit.program_id = Programs.program_id
)
INNER JOIN dbo.Clients
ON (
Clients.client_id = ClientVisit.client_id
)
WHERE (
ClientVisit.rev_timeout BETWEEN '20160401 11:40:00.000' AND '20160415 11:40:16.000'
AND Programs.program_desc IN ('Off Panel')
AND ClientVisit.non_billable = 0
AND ClientVisit.cptcode NOT IN ('00000', '0124', '100', '1001', '101', '102', '103', '80100', '9079', '99999')
AND Employees.num3 IS NOT NULL
)
GROUP BY
CASE
WHEN Clients.age <= 18 THEN 'Youth'
ELSE 'Adult'
END,
CASE
WHEN Employees.first_name IS NULL
OR Employees.first_name = 'x' THEN Employees.last_name
WHEN Employees.credentials IS NULL THEN Employees.last_name + ', ' + Employees.first_name
ELSE Employees.last_name + ', ' + Employees.first_name + ' - ' + Employees.credentials
END,
DATEDIFF(d, ClientVisit.rev_timeout, ClientVisit.signature_datetime),
ClientVisit.cptcode,
Clients.last_name + ', ' + Clients.first_name,
Clients.client_id,
Employees.address1 + ' ' + Employees.city + ', ' + Employees.state + ' ' + Employees.zip,
Employees.work_phone,
ClientVisit.duration,
ClientVisit.visittype,
ClientVisit.rev_timeout,
ClientVisit.timein,
Employees.num3
Gives me the Error:
Conversion failed when converting the varchar value 'H2019' to data
type int.
I'm unable to locate specifically where this conversion is taking place and what could be a possible fix.
EDIT: Located problem in cptcode column which has alphanumeric entries. However, changing date ranges in WHERE clause gives results for some dates and not for others.
when you use
"ClientVisit"."cptcode" = 90791
the data type of 90791 will be integer. If you replace it with
"ClientVisit"."cptcode" = '90791'
both sides of the equation will be characters. You can also do something like:
"ClientVisit"."cptcode" = CAST(90791 AS VARCHAR(20))
The reasons for your problem is that SQL Server will do an implicit conversion. In this case to integer as Integer has a higher Data Type Precedence than (n)(var)char data types.
Of course I do not know your data but I guess there are data ranges where there is only numeric values for cptcode. So your code will work for them but not if you hit something like e.g. H006
Hope that helps ;-)
Comment out all the colums in the select segment and start ucommenting them one by one while executing the select each time. When you uncomment the problematic line, you'll find the source of the error.
BTW: I'm guessing the problem is in "ClientVisit"."cptcode" = 90791.
Here's a sample of my code:
SET #variable_out =
'Report: '
+ CASE WHEN (SELECT name FROM person WITH(NOLOCK) WHERE person_id = #person_id) != ''
THEN 'Name: ' + (SELECT name FROM person WITH(NOLOCK) WHERE person_id = #person_id) + CHAR(13)+CHAR(10)
ELSE 'Name: not found' + CHAR(13)+CHAR(10)
END
+ CASE WHEN (SELECT home_phone FROM person WITH(NOLOCK) WHERE person_id = #person_id) != ''
THEN 'Phone #: ' + (SELECT home_phone FROM person WITH(NOLOCK) WHERE person_id = #person_id) + CHAR(13)+CHAR(10)
ELSE 'Phone #: not found' + CHAR(13)+CHAR(10)
END
etc...
As you can see, I am redundantly performing two selects for each CASE WHENE... of the variable that I am constructing, and I would love to collapse this down to only one select for each line.
The only solution I know of would be to create a unique variable for CASE WHEN..., run all of the selects before hand, and then if the variables aren't empty, concat them into #variable_out.
Is there a more clever way to accomplish this?
DECLARE #name the_same_datatype_as_name_field_from_person_table --Ex. VARCHAR(100)
,#home_phone the_same_datatype_as_homephone_field_from_person_table; --Ex. VARCHAR(15)
SELECT #name = NULLIF(p.name,''), #home_phone = NULLIF(p.home_phone,'')
FROM person p --WITH(NOLOCK)
WHERE p.person_id = #person_id;
SET #variable_out =
'Report: '
+ ISNULL('Name: ' + #name, 'Name: not found')
+ CHAR(13)+CHAR(10)
+ ISNULL('Phone #: ' + #home_phone, 'Phone #: not found')
+ CHAR(13)+CHAR(10);
Note:
or, you can use for #name & #home_phone variables the same data type like as #variable_out variable (ex. VARCHAR).
NOLOCK pros & cons.
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