I have the table as defined below (that is output by a view). As can be seen, there are many values as -0.01 that are just noise to the user.
Given there are 48 columns, creating 48 seperate CASE statemnts would yield very slow results.
Can a mass case be used to clean up this data, ie:
select
CASE
WHEN * = -0.01
THEN 0
ELSE *
END
from aTable
Data:
If your concern is the time it takes to write the code, you could use Dynamic SQL to help write it for you by referencing the sys tables:
SELECT 'CASE WHEN ' + columns.NAME + ' = -.01 THEN 0 ELSE '
+ columns.NAME + ' END AS ' + columns.NAME + ', ' AS Dynamic_SQL
FROM sys.columns
INNER JOIN sys.objects ON objects.object_id = columns.object_id
WHERE objects.NAME = 'aTable'
ORDER BY column_id
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 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'.
I need to concatenate two fields from different tables, based on the results of a case when. This is what I have so far -
Select
CASE WHEN [Record] = 'Criteria' then (dbo.[Table A].[Field] + ' ' + dbo.[Table B].[Field]) AS 'Name'
Else ' '
INNER JOIN dbo.[Table] ON dbo.[Table].[Field] = dbo.[Table].[Field]
From [Table]
But when I run this, I keep getting the error
Msg 156, Level 15, State 1, Line 2 Incorrect syntax near the keyword
'AS'.
I don't know if my statement is just totally wrong or I have missed something in it.
Thanks
Select
CASE WHEN [Record] = 'Criteria' then (dbo.[Table A].[Field] + ' ' + dbo.[Table B].[Field])
Else ' '
end AS 'Name'
From [Table]
INNER JOIN dbo.[Table] ON dbo.[Table].[Field] = dbo.[Table].[Field]
Then problem with your case statement;
CASE WHEN [Record] = 'Criteria' then (dbo.[Table A].[Field] + ' ' + dbo.[Table B].[Field])
Else ' ' end AS 'Name'
Keep in mind that using + to concatenating string you will get NULL if tab_a.[Field] or tab_b.[Field] is NULL.
Second use aliases to table names.
SELECT
[Name] = CASE
WHEN [Record] = 'Criteria' THEN (tab_a.[Field] + ' ' + tab_b.[Field])
ELSE ' '
END
FROM [dbo].[Table A] AS tab_a
JOIN [dbo].[Table B] AS tab_b
ON tab_a.[Field] = tab_b.[Field]
I am create a stored procedure in SQL and I get the following error when I execute the query:
Conversion failed when converting the nvarchar value '11021,78542,12456,24521' to data type int.
Any idea why?
SELECT
A.Art_ID, A.Title
FROM
Art A
INNER JOIN
Iss I ON A.Iss_ID = I.Iss_ID
INNER JOIN
Sections S ON A.Section_ID = S.Section_ID
INNER JOIN
iPadSec IPS ON A.Sec_ID = IPS.Sec_ID
WHERE
A.Art_ID IN (SELECT CAST(Art_IDs AS int) /***error happens here***/
FROM Book_Art b
WHERE Sub_ID = 68)
AND I.Iss > dateadd(month, -13, getdate())
AND A.Active = 1
AND IPS.Active = 1
AND A.PDate <= getdate()
ORDER BY
PDate DESC, Art_ID DESC;
You cannot do what you want using in. First, it is a really bad idea to store ids in lists in strings. You should be using a junction table.
That said, sometimes this is necessary. You can rewrite this line of code as:
EXISTS (SELECT 1 /***error happens here***/
FROM Book_Art b
WHERE Sub_ID = 68 AND
',' + Art_IDs + ',' LIKE '%,' + cast(A.Art_ID as varchar(255)) + ',%'
)
However, the performance would generally be on the lousy side and there is little prospect of speeding this up without fixing the data structure. Use a junction table instead of a string to store lists.
Adding this line works for me.
declare #ids varchar(1000)
select #ids = art_ids from book_art where sub_id = #Sub_ID
EXECUTE ( 'SELECT A.Art_ID, A.Title'
+ ' FROM Art A'
+ ' INNER JOIN Iss I ON A.Iss_ID = I.Iss_ID'
+ ' INNER JOIN Sections S ON A.Section_ID = S.Section_ID'
+ ' INNER JOIN iPadSec IPS ON A.Sec_ID = IPS.Sec_ID'
+ ' WHERE A.Art_ID IN (' + #ids + ')'
+ ' AND I.Iss > dateadd(month, -13, getdate())'
+ ' AND A.Active = 1'
+ ' AND IPS.Active = 1'
+ ' AND A.PDate <= getdate()'
+ ' ORDER BY PDate DESC,'
+ ' Art_ID DESC;'
)
END
Thank you all for your help :)
Please help me with this. I am totally stuck. I have coders block or something.
I have the following table
ID Name Cost Included
---- ---------- ------- ----------
1 Package1 10.00 Yes
2 Package2 20.00 No
3 Package3 20.00 Yes
I would like to crosstab this information, to display like the following example,there will be more columns in the table.
Type Package1 Package2 Package3
----- ------------ ----------- ----------
Name Package1 Package2 Package3
Cost 10.00 20.00 30.00
Included Yes No Yes
It seems to me that you are trying to build a product comparison list. If this is true, you might unpivot the table first and then join individual records together.
The 'transponded' part unpivots the columns. All columns must be of compatible types or converted to one. I choose varchar(100). transponded returns table with three columns, ID from ProductInfo, Type as column name and Value as value of corresponding column.
Select part joins together info on as many product as demanded by adding another left join transponded tn on t1.Type = tnType and tn.ID = #parametern. This part seems as a hassle, but when I tried to do this part with pivot I failed to get column in proper order - pivot sorted names in Type. It would however demand dynamic sql generation. This solution is fixed providing that you add enough joins for maximum products you wish to compare at once. I belive it would not be over 5.
=1, =2 and =3 should be replaced by parameters. The query should be hosted in stored procedure.
; with transponded as
(
select ID, Type, Value
from
(
select ID,
Name,
cast (Cost as varchar(100)) Cost,
cast (case when Included = 1 then 'Yes' else 'No' end as varchar(100)) Included
from ProductInfo
) p
unpivot (Value for Type in (Name, Cost, Included) ) a
)
select t1.Type,
t1.Value Product1,
t2.Value Product2,
t3.Value Product3
from transponded t1
left join transponded t2
on t1.Type = t2.Type
and t2.id = 2
left join transponded t3
on t1.Type = t3.Type
and t3.id = 3
where t1.id = 1
In short, transpond one record at time and join to another transponded record by Type column.
Oh, and here is a Sql Fiddle playground.
There is no easy way to do this, as the pivot will need to be aggregated by column. Given that adding columns to the input table would cause a maintenance issue where these values will not be presented to the output until the code is changed wherever it is used, I'd say you're probably best doing it once with a stored procedure, which will dynamically generate the output you're looking for based on the schema of the input table.
I have demonstrated how this can be done, using the data you have supplied. This data is stored in a temp table (not #temp, because the stored proc won't work with temporary tables), populated thus:
CREATE TABLE temp (
_key int,
package_name varchar(50),
cost float,
included bit
)
INSERT INTO temp VALUES(1,'Package1', 10.00, 1)
INSERT INTO temp VALUES(2,'Package2', 20.00, 0)
INSERT INTO temp VALUES(3,'Package3', 20.00, 1)
The stored procedure retrieves a list of values based on the #pivot_field parameter, and uses these values as a column list to be inserted after the "Type" field. It then unions the pivot field and all other fields together to generate the rows, pivoting one column at a time. The procedure is as follows:
CREATE PROCEDURE usp_get_pivot (#table_name nvarchar(255), #pivot_field nvarchar(255)) AS
BEGIN
CREATE TABLE #temp (val nvarchar(max))
DECLARE #sql NVARCHAR(MAX), #cols NVARCHAR(MAX), #col NVARCHAR(255)
SET #sql = 'SELECT DISTINCT ' + #pivot_field + ' FROM ' + #table_name
INSERT INTO #temp EXEC sp_executesql #sql;
SET #cols = (SELECT '[' + val + '],' FROM #temp FOR XML PATH(''))
SET #cols = SUBSTRING(#cols, 1, LEN(#cols)-1)
SET #SQL = N'SELECT ''' + #pivot_field + ''' as [type], *
FROM (SELECT ' + #pivot_field + ', ' + #pivot_field + ' as ' + #pivot_field + '1 FROM ' + #table_name + ') AS source_table
PIVOT (max(' + #pivot_field + '1) FOR ' + #pivot_field + ' IN (' + #cols + ')) AS pivot_table'
DECLARE csr CURSOR FOR
SELECT c.name FROM sys.columns c, sys.objects o
WHERE c.object_id = o.object_id AND o.name = #table_name
AND c.name <> #pivot_field
ORDER BY column_id
OPEN csr
FETCH NEXT FROM csr INTO #col
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = #sql + ' UNION ALL
SELECT ''' + #col + ''' as [type], *
FROM (SELECT ' + #pivot_field + ', CAST(' + #col + ' AS VARCHAR) AS ' + #col + ' FROM ' + #table_name + ') AS source_table
PIVOT (max(' + #col + ') FOR ' + #pivot_field + ' IN (' + #cols + ')) AS pivot_table'
FETCH NEXT FROM csr INTO #col
END
CLOSE csr
DEALLOCATE csr
DROP TABLE #temp
EXEC sp_executesql #sql
END
You should be able to simply copy and paste the procedure into management studio, create the data is shown above and execute the procedure with:
EXEC usp_get_pivot 'temp', 'package_name'
If number of packages is not static there is no option for you I think. PIVOT clause can produce only static/defined number of columns.
You may do some table-to-table rewriting using multiple statements - but still you have to face with static number of columns.
But you may set it to for example to 10 and then display up to 10 packages, having NULL-s in rest of columns if there are less packages.
You may also use dynamic SQL to have dynamic number of columns - but it will be a headache.
If you're going to export this data to Excel - do not pivot it at SQL - do a transposition in Excel (it's under "paste special").
Basically what i have at this stage is the following.
SELECT [Type],
MAX(Beginner) AS [Beginner],
MAX(Intermediate) AS [Intermediate],
MAX(Advanced) AS [Advanced]
FROM
(
SELECT
'Name' AS TYPE,
CASE WHEN Name='Beginner' THEN Name END AS [Beginner],
CASE WHEN Name='Intermediate' THEN Name END AS [Intermediate],
CASE WHEN Name='Advanced' THEN Name END AS [Advanced]
FROM Administration.Package
UNION ALL
SELECT
'Price' AS TYPE,
CASE WHEN Name='Beginner' THEN CAST(Price AS VARCHAR) END AS [Beginner],
CASE WHEN Name='Intermediate' THEN CAST(Price AS VARCHAR) END AS [Intermediate],
CASE WHEN Name='Advanced' THEN CAST(Price AS VARCHAR) END AS [Advanced]
FROM Administration.Package
)A
GROUP BY [Type]
But it does not feel right to have the union for each and every column.