Issue with NULL values in creating a view in sql table - sql

I am working on a SQL View over a table with different values in it. I am trying to combine two columns in a table to be displayed in a single column of a view separated by a '/'.
For Example I have two columns in a table named In and inVolume with values for 'In' being
1,2,NULL
and for 'inVolume ' being
NULL, 100, 200
and the results I was expecting are 1/(NULL or Empty), 2/100, (NULL or Empty)/200 but when I created the view and ran it the results were (NULL or Empty), 2/100, (NULL or Empty). The issue being it is making the column in view as NULL if any of the columns In or inVolume in the table are NULL.
I created the following view
SQL View
CREATE VIEW [dbo].[Result]
AS
with CTE as(
SELECT distinct
CC.Product,
CC.Term,
CC.TermID,
iCC.In,
iCC.Out,
iCC.inVolume,
iCC.outVolume
FROM Cust CC
CROSS APPLY (SELECT TOP 3
In,
Out,
inVolume,
outVolume
FROM Cust iCC
WHERE CC.Term = iCC.Term and CC.Product = iCC.Product
ORDER BY iCC.In DESC, iCC.Out ASC) iCC)
select Product, Term, cast(inVolume as varchar(99)) + '/' + cast(In as varchar(99)) as value, inlabel as label from CTE
union
select Product, Term, cast(Out as varchar(99)) + '/' + cast(outVolume as varchar(99)), outlabel from CTE
GO
Any better way to deal with this?

Replace this:
cast(outVolume as varchar(99))
Whit this:
cast((CASE WHEN outVolume IS NULL THEN 0 ELSE outVolume END) as varchar(99))
On every field. Hope this help.

Use IsNull to enable nullable-column concatentation
select Product, Term,
IsNull(inVolume, '(NULL OR EMPTY)', cast(inVolume as varchar(99))) + '/' +
IsNull(In, '(NULL OR EMPTY)', cast(In as varchar(99))) as value,
inlabel as label from CTE
UNION
select Product, Term,
IsNull(Out, '(NULL OR EMPTY)', cast(Out as varchar(99))) + '/' +
IsNull(outVolume, '(NULL OR EMPTY)', cast(outVolume as varchar(99))),
outlabel from CTE

Replace
cast(inVolume as varchar(99)) + '/' + cast(In as varchar(99))`
with
cast(inVolume as varchar(99)) + '/'
+ ISNULL(cast(In as varchar(99)), '(NULL OR EMPTY)')`
and
cast(Out as varchar(99)) + '/' + cast(outVolume as varchar(99))`
with
cast(Out as varchar(99)) + '/'
+ ISNULL(cast(outVolume as varchar(99)), '(NULL OR EMPTY)')

Related

SQL - Remove Duplicate value between two columns

I'm looking a simple way to remove an unwanted Duplicate value.
The Dupe is part of a reference to another column, and not within the column itself, but the column I want to remove the dupe value from is multi-delimited with other values.
Here is an example table:
ID,Thing
Dog,Cat;Dog;Bird
Snake,Horse;Fish;Snake
Car,Car;Bus;Bike
As you can see Dog,Snake,Car are the values I need to remove from the Thing column.
Output:
ID,Thing
Dog,Cat;Bird
Snake,Horse;Fish
Car,Bus;Bike
Is there a way to match within a multidelimited field and pull out the exact match?
I'm using SQL Server MGMT studio. Thanks.
WITH CTE AS
(
SELECT ID, Thing, ROW_NUMBER() OVER (PARTITION BY Thing) AS rn
)
DELETE
FROM CTE
WHERE rn > 1
I believe this will do it. Test first by running just the CTE part of the query so you can see what rn is.
Your question and sample data is not very clear. I think what you want is to remove anything from the second column that is in the first column, in which case you can try using replace
select Id,
replace(replace(thing,id,''),';;',';')
from table
Storing multi-value elements in a column is never a good idea and is a conflict of interest with the relational data model; it pretty much always causes problems at some point.
What you can do is concatenate a leading and a trailing ; to the value of Thing and then replace the value of ID with an empty string.
Then remove the leading and trailing ;.
If your version of SQL Server is 2017+, you can use the function TRIM():
SELECT Id,
TRIM(';' FROM REPLACE(';' + Thing + ';', ';' + ID + ';', ';')) Thing
from tablename;
For previous versions use SUBSTRING():
SELECT Id,
SUBSTRING(
REPLACE(';' + Thing + ';', ';' + ID + ';', ';'),
2,
LEN(REPLACE(';' + Thing + ';', ';' + ID + ';', ';')) - 2
) Thing
from tablename;
If you want to update the table:
UPDATE tablename
SET Thing = TRIM(';' FROM REPLACE(';' + Thing + ';', ';' + ID + ';', ';'));
or:
UPDATE tablename
SET Thing = SUBSTRING(
REPLACE(';' + Thing + ';', ';' + ID + ';', ';'),
2,
LEN(REPLACE(';' + Thing + ';', ';' + ID + ';', ';')) - 2
);
See the demo.
I don't really understand what "multi-delimited" means with respect to a string. In your context it seems to suggest that you might have different types of delimiters. It definitely does mean that you have a really poor data model. If you want to remove the id from the things column, then my first suggestion is to fix the delimiters.
In SQL Server, you could use:
select t.*,
(select string_agg(s.value, ';')
from string_split(replace(t.things, ',', ';'), ';') s
where s.value <> t.id
) as new_things
from t;
If the delimited have some intrinsic meaning (did I mentioned that you should fix the data model?), then you can use a more brute force approach. Here is one method:
select t.*,
(case when things = id then ''
when things like concat(id, '[,;]%')
then stuff(things, 1, len(id) + 1, '')
when things like concat('%[,;]', id)
then left(things, len(things) - len(id) - 1)
when things like concat('%[,;]', id, '[,;]%')
then stuff(things, patindex(concat('%[,;]', id, '[,;]%'), things), len(id) + 1, '')
else things
end)
from t;
Here is a db<>fiddle.
Your Question is a good one. I used simple case statement to get the answer. It is CHARINDEX that helped to find the location of the value in Id column and then identified the position of the value in id and according to the position, string was replaced by required values.
--Preparing the table
SELECT *
INTO t
FROM (VALUES
('Dog', 'Cat;Dog;Bird'),
('Snake', 'Horse;Fish;Snake'),
('Car', 'Car;Bus;Bike')
) v(id, things)
--Query
SELECT id
,CASE WHEN CHARINDEX(reverse(id), reverse(things), 1) = 1 THEN REPLACE(things,';'+id ,'')
WHEN CHARINDEX(id, things, 1) < LEN(things) AND CHARINDEX(id, things, 1) > 1 THEN REPLACE(things, id +';' ,'')
WHEN CHARINDEX(id, things, 1) = 1 THEN REPLACE(things, id +';' ,'')
ELSE 'End'
END as [things]
FROM t

Query help consolidating two tables

I have two tables (same structure) from two different databases that I'd like to consolidate using a single query if possible.
I'm trying to retrieve all distinct serial numbers and their item name, and two category identifiers. The serial number is stored in 4 fields though. The other problem is the name and category field wont always be the same between the two tables (even though they should be - but that's another issue all together). So, I want the query to return distinct SNs and the name and cat fields from the first table.
So I started with:
SELECT
LEFT(NUMBR_1,4) + '-' + LEFT(NUMBR_2,4) + '-' + LEFT(NUMBR_3,3) + '-' + LEFT(NUMBR_4,5) AS SN
,DESCR
,TYP
,ATNUM
FROM DB1.dbo.table1
UNION
SELECT
LEFT(NUMBR_1,4) + '-' + LEFT(NUMBR_2,4) + '-' + LEFT(NUMBR_3,3) + '-' + LEFT(NUMBR_4,5) AS SN
,DESCR
,TYP
,ATNUM
FROM DB2.dbo.table2
From there I'd manually complete the consolidation in Excel and feed that data into the necessary report. I was hoping to get the final result using just SQL, but doing so is outside of my skill set.
I wrapped the above query in another select to get distinct or group by SN - which gets me the final consolidated list of SN. However, because those values themselves weren't something I could use to then query the other fields from the first table (at least that I could figure out), I wasn't sure how to proceed. Any help would be appreciated. Thanks.
SELECT
LEFT(NUMBR_1,4) + '-' + LEFT(NUMBR_2,4) + '-' + LEFT(NUMBR_3,3) + '-' + LEFT(NUMBR_4,5) AS SN,
,coalesce(t1.DESCR, t2.DESCR) DESCR,
,coalesce(t1.TYP, t2.TYP) TYP
,coalesce(t1.ATNUM, t2.ATNUM) ATNUM
FROM DB1.dbo.table1 t1
FULL JOIN DB2.dbo.table2 t2 ON
t1.NUMBR_1 = t2.NUMBR_1 AND t1.NUMBR_2 = t2.NUMBR_2 AND t1.NUMBR_3 = t2.NUMBR_3 AND t1.NUMBR_4 = t2.NUMBR_4
Similar answer to Joel who beat me to it, though this will actually run. Just swap out #t1 and #t2 for your table names. FULL JOINs return all records from both tables, and where there is no match, returns NULLs for one side and the unmatched values for the other:
declare #t1 table (numbr_1 int
,numbr_2 int
,numbr_3 int
,numbr_4 int
,descr nvarchar(50)
,typ nvarchar(50)
,atnum int
);
declare #t2 table (numbr_1 int
,numbr_2 int
,numbr_3 int
,numbr_4 int
,descr nvarchar(50)
,typ nvarchar(50)
,atnum int
);
insert into #t1 values
(1,1,1,1,'d1','t1',1)
,(1,1,1,2,'d2','t1',1)
,(1,1,1,3,'d3','t2',2)
,(1,1,2,1,'d4','t2',3)
,(1,1,2,2,'d5','t2',4);
insert into #t2 values
(1,1,1,1,'d6','t1',1)
,(1,1,1,2,'d7','t3',1)
,(1,2,1,3,'d8','t4',2)
,(1,2,2,1,'d9','t4',3)
,(1,2,2,2,'d5','t2',4);
select coalesce(left(t1.numbr_1,4) + '-' + left(t1.numbr_2,4) + '-' + left(t1.numbr_3,4) + '-' + left(t1.numbr_4,4)
,left(t2.numbr_1,4) + '-' + left(t2.numbr_2,4) + '-' + left(t2.numbr_3,4) + '-' + left(t2.numbr_4,4)
) as ID
,coalesce(t1.descr,t2.descr) as descr
,coalesce(t1.typ,t2.typ) as typ
,coalesce(t1.atnum,t2.atnum) as atnum
from #t1 t1
full join #t2 t2
on(t1.numbr_1 = t2.numbr_1
and t1.numbr_2 = t2.numbr_2
and t1.numbr_3 = t2.numbr_3
and t1.numbr_4 = t2.numbr_4
);

SUM in stored procedure

I'm trying to calculate the SUM of MEMDISC but I keep getting this error on a stored procedure
Column 'dbo.ZZ.CNUM is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
This is my code
select
CNUM,
DESCRIPT,
ZCONTDESC,
substring(str(OCCDATE),9,2) + '/'+ substring(str(OCCDATE),7,2) + '/' + substring(str(OCCDATE),3,4),
COWNNUM,
CAST((CAST(substring(str(ANVDATE),3,4) AS INT) -1) AS CHAR(4))+'-' +substring(str(ANVDATE),3,4),
sum(MEMDISC)
from
dbo.ZZ
What it should look like?
As suggested in comments by #Lucky and #LukStorms, when you use aggregation functions you need to GROUP BY all fields which are not included in an aggregation function. In your case you need something like this:
select CNUM, DESCRIPT, ZCONTDESC,
substring(str(OCCDATE),9,2) + '/'+ substring(str(OCCDATE),7,2) + '/' + substring(str(OCCDATE),3,4),
COWNNUM,
CAST((CAST(substring(str(ANVDATE),3,4) AS INT) -1) AS CHAR(4))+'-' +substring(str(ANVDATE),3,4),
sum(MEMDISC)
from dbo.ZZ
group by CNUM, DESCRIPT, ZCONTDESC,
substring(str(OCCDATE),9,2) + '/'+ substring(str(OCCDATE),7,2) + '/' + substring(str(OCCDATE),3,4),
COWNNUM,
CAST((CAST(substring(str(ANVDATE),3,4) AS INT) -1) AS CHAR(4))+'-' +substring(str(ANVDATE),3,4),

T-SQLpad leading zeros in string for values between "." character

I want to convert numbers to 3 decimal places for each number between the character ".". For example:
1.1.5.2 -> 001.001.005.002
1.2 -> 001.002
4.0 -> 004.000
4.3 ->004.003
4.10 -> 004.010
This is my query:
SELECT ItemNo
FROM EstAsmTemp
This is fairly easy once you understand all the steps:
Split the string into the individual data points.
Convert the parsed values into the format you want.
Shove the new values back into a delimited list.
Ideally you shouldn't store data with multiple datapoints in a single intersection like this but sometimes you just have no choice.
I am using the string splitter from Jeff Moden and the community at Sql Server Central which can be found here. http://www.sqlservercentral.com/articles/Tally+Table/72993/. There are plenty of other decent string splitters out there. Here are some excellent examples of other options. http://sqlperformance.com/2012/07/t-sql-queries/split-strings.
Make sure you understand this code before you use it in your production system because it will be you that gets the phone call at 3am asking for it to be fixed.
with something(SomeValue) as
(
select '1.1.5.2' union all
select '1.2' union all
select '4.0' union all
select '4.3' union all
select '4.10'
)
, parsedValues as
(
select SomeValue
, right('000' + CAST(x.Item as varchar(3)), 3) as NewValue
, x.ItemNumber as SortOrder
from something s
cross apply dbo.DelimitedSplit8K(SomeValue, '.') x
)
select SomeValue
, STUFF((Select '.' + NewValue
from parsedValues pv2
where pv2.SomeValue = pv.SomeValue
order by pv2.SortOrder
FOR XML PATH('')), 1, 1, '') as Details
from parsedValues pv
group by pv.SomeValue
I decided to change it in the presentation layer, per Zohar Peled's comment.
You did not mention the number of '.' separator a column can have. I assume, the max is 4 and the solution is below.
SELECT STUFF(ISNULL('.' + RIGHT('000' + PARSENAME(STRVALUE,4),4),'') + ISNULL('.' + RIGHT('000' + PARSENAME(STRVALUE,3),4) ,'') + ISNULL('.' + RIGHT('000' + PARSENAME(STRVALUE,2),4) ,'') + ISNULL('.' + RIGHT('000' + PARSENAME(STRVALUE,1),4),''),1,1,'')
FROM (VALUES('1.1.5.2'), ('1.2'), ('4.0'),('4.3'), ('4.10')) A (STRVALUE)

SQL Stored Procedure - replace a result column with value from existing table column

I'm having trouble figuring out how to replace a value on a stored procedure result based on values from another table. I have a table [LOG] that is formatted as such:
TIME STAMP, TAG, DESCRIPTION, EVENTCODE, SUBEVENTCODE
30-Aug-2013 10:14:10, TAG X, HI TEMP FAULT, 3, 16
30-Aug-2013 10:12:10, TAG Y, HI PRESS FAULT, 3, 16
...
And another table [EVENTS] which basically describes what the EVENTCODE is:
EVENT, DESCRIPTION
1, FAULT
2, LOGIC
3, ALARM
I would like to have the stored procedure retrieve 2000 entries (rows) of the 1st table and, instead of showing EVENTCODE as a number, display the description contained in the 2nd table on the result.
e.g:
TIME STAMP, TAG, DESCRIPTION, EVENTCODE, SUBEVENTCODE
30-Aug-2013 10:14:10, TAG X, HI TEMP FAULT, ALARM, 16
Reason is I have another software that interacts with the result of the stored procedure, and wouldn't like to create another table to hold these results within the database.
Here is what the stored procedure looks like so far:
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[Get2kEvents]
AS
BEGIN
SELECT TOP 2000 CAST(datepart(day,TIME_STAMP) as char(2)) + '-' +
CAST(datename(month,TIME_STAMP) as char(3)) + '-' +
CAST(datepart(year,TIME_STAMP) as char(4))+ ' ' + CONVERT(varchar,TIME_STAMP,108)
as 'TIME STAMP',
[TAG],
[DESCRIPTION],
[EVENTCODE],
[SUBEVENTCODE]
FROM [Arc_DB].[dbo].[LOG]
ORDER BY TIME_STAMP DESC
END
GO
I appreciate your assistance. Sorry if this is too basic, but I wasn't able to figure out a solution for this while browsing this and other websites.
Cheers.
TM
You want to join the two tables. ie:
SELECT TOP 2000 CAST(datepart(day,TIME_STAMP) as char(2)) + '-' +
CAST(datename(month,TIME_STAMP) as char(3)) + '-' +
CAST(datepart(year,TIME_STAMP) as char(4))+ ' ' + CONVERT(varchar,TIME_STAMP,108)
as 'TIME STAMP',
[TAG],
[DESCRIPTION],
Events.Description,
[SUBEVENTCODE]
FROM [Arc_DB].[dbo].[LOG]
inner join events on log.eventcode = events.event
ORDER BY TIME_STAMP DESC
Use a JOIN:
SELECT TOP 2000 CAST(datepart(day,TIME_STAMP) as char(2)) + '-' +
CAST(datename(month,TIME_STAMP) as char(3)) + '-' +
CAST(datepart(year,TIME_STAMP) as char(4))+ ' ' + CONVERT(varchar,TIME_STAMP,108)
as 'TIME STAMP',
[TAG],
L.[DESCRIPTION],
E.[DESCRIPTION],
[SUBEVENTCODE]
FROM [Arc_DB].[dbo].[LOG] L
INNER JOIN [Arc_DB].[dbo].[EVENTS] E ON E.EVENT = L.EVENTCODE
ORDER BY TIME_STAMP DESC
I would use a left join and check for missing codes:
SELECT TOP 2000 CAST(datepart(day,TIME_STAMP) as char(2)) + '-' +
CAST(datename(month,TIME_STAMP) as char(3)) + '-' +
CAST(datepart(year,TIME_STAMP) as char(4))+ ' ' + CONVERT(varchar,TIME_STAMP,108)
as 'TIME STAMP',
[TAG],
[DESCRIPTION],
CASE WHEN EVENTS.DESCRIPTION IS NULL
THEN 'UNKNOWN CODE '+CAST(LOG.EVENTCODE AS VARCHAR(20))
ELSE EVENTS.DESCRIPTION
END AS [Event Type],
[EVENTCODE],
[SUBEVENTCODE]
FROM LOG
LEFT JOIN EVENTS ON EVENTS.EVENT= LOG.EVENTCODE
ORDER BY TIME_STAMP DESC