SQL replace 2-3 letter value in string - sql

I have a table with contacts and their key skills where values are divided by a semicolon:
And I can't figure out how to replace short key skills without harming the existing longer skills. E.g. UI is included in word Building
For more than 4 letters I'm using below SQL script to replace value1 (#current) for value2 (#replace) just fine
DECLARE #current varchar(50) = 'UI'
DECLARE #replace varchar(50) = 'New Skill'
UPDATE database.dbo.contact
SET key_skill = CASE
WHEN key_skill LIKE '%'+#replace+'%'+#current THEN REPLACE(key_skill, ';'+#current, '')
WHEN key_skill LIKE '%'+#current+'%'+#replace THEN REPLACE(key_skill, #current+';', '')
WHEN (key_skill LIKE '%'+#replace+'%'+#current+'%') OR (key_skill LIKE '%'+#current+'%'+#replace+'%') THEN REPLACE(key_skill, #current+';', '')
WHEN key_skill LIKE '%'+#current+'%' THEN REPLACE(key_skill, #current, #replace)
ELSE key_skill END
FROM database.dbo.contact
WHERE (key_skill LIKE '%'+#current+'%')

If it is at all possible you should change your design as soon as possible. There is almost never a good reason to store lists as delimited strings in a database. Databases already have the perfect structure for storing lists, they are called tables. A second table that links contacts to skills will be really useful here. Something like this:
CREATE TABLE dbo.Contact
(
ContactID INT IDENTITY (1, 1) NOT NULL,
Name VARCHAR(255) NOT NULL,
CONSTRAINT PK_Contact__ContractID PRIMARY KEY (ContactID)
);
CREATE TABLE dbo.KeySkill
(
KeySkillID INT IDENTITY (1, 1) NOT NULL,
Name VARCHAR(50) NOT NULL,
CONSTRAINT PK_KeySkill__KeySkillID PRIMARY KEY (KeySkillID)
);
CREATE TABLE dbo.ContactKeySkill
(
ContactID INT NOT NULL,
KeySkillID INT NOT NULL,
CONSTRAINT PK_ConactKeySkill__ContactID_KeySkillID PRIMARY KEY (ContactID, KeySkillID),
CONSTRAINT FK_ContactKeySill__ContactID FOREIGN KEY (ContactID) REFERENCES dbo.Contact (ContactID),
CONSTRAINT FK_ContactKeySill__KeySkillID FOREIGN KEY (KeySkillID) REFERENCES dbo.KeySkill (KeySkillID)
);
With this structure in place everything else becomes significantly easier. You can recreate your existing format if needed as follows:
SELECT c.ContactID, c.Name, Skills = STRING_AGG(ks.Name, ';')
FROM dbo.Contact AS c
INNER JOIN dbo.ContactKeySkill AS cks
ON cks.ContactID = c.ContactID
INNER JOIN dbo.KeySkill AS ks
ON ks.KeySkillID = cks.KeySkillID
GROUP BY c.ContactID, c.Name;
You are also in complete control of ordering and filtering (with indexes), and data integrity (no duplicates, or typos etc).
Adding/removing skills becomes as simple as inserting/deleting rows rather than having to do any string manipulation.
And if you decided you wanted to rename a skill, e.g. "UI" with "User Interface" well, again that is really really easy in a properly designed database:
UPDATE dbo.KeySkill
SET Name = 'User Interface'
WHERE Name = 'UI';
Because you have now separated all your data, you can be certain that when you update UI there are no side effects because that is the only value stored in that field.
Working Demo on db<>fiddle
If you are not in control of your design and can't make these changes, then the following should work for you:
STUFF(key_skill,
CHARINDEX(CONCAT(';', #current, ';'),CONCAT(';', key_skill, ';')),
LEN(#current),
#replace);
The premise is that if you add ; to the start and the end of both your key_skill string and your #current parameter, then it doesn't matter whether the term is at the start or the end of the string, you would be looking for ;UI; in ;UI;PHP;Building;, so the search term no longer matches in building.
It is easier to use STUFF() here rather than REPLACE(), just so you don't have to actually build a string with semi-colons on the end, then remove them at the end. All you need is to use CHARINDEX to find out where the skill starts in the string (2nd argument in stuff), the length of the skill (3rd argument), and use this as the starting point to "stuff" your new string in (4th argument).
Demo
CREATE TABLE #T (Contact VARCHAR(255), key_skill VARCHAR(255));
INSERT #T(Contact, key_skill)
VALUES
('John Doe', 'AI;UI;ONC;BI;PHP'),
('Craig Smith', 'UI;PHP;Building'),
('Loren Paul', 'AI;UI');
DECLARE #current VARCHAR(50) = 'UI',
#replace VARCHAR(50) = 'New Skill'
UPDATE #T
SET key_skill = STUFF(key_skill,
CHARINDEX(CONCAT(';', #current, ';'),CONCAT(';', key_skill, ';')),
LEN(#current),
#replace)
WHERE CHARINDEX(CONCAT(';', #current, ';'),CONCAT(';', key_skill, ';')) > 0;
SELECT *
FROM #T;
ADENDUM
Since you can't change your data structure a more robust method of doing this will be to deconstruct your delimited list (using STRING_SPLIT()), then make your changes, then reconstruct it again (using STRING_AGG()), e.g.
CREATE TABLE #T (Contact VARCHAR(255), key_skill VARCHAR(255));
INSERT #T(Contact, key_skill)
VALUES
('John Doe', 'AI;UI;ONC;BI;PHP'),
('Craig Smith', 'UI;PHP;Building'),
('Loren Paul', 'AI;UI');
DECLARE #current VARCHAR(50) = 'UI',
#replace VARCHAR(50) = 'New Skill'
UPDATE t
SET t.key_skill = s.NewList
FROM #T AS t
CROSS APPLY
( SELECT STRING_AGG(Value, ';')
FROM ( SELECT Value
FROM STRING_SPLIT(t.key_skill, ';') AS s
WHERE s.value <> #current
UNION
SELECT #replace
WHERE #replace <> ''
) AS s
) AS s (NewList);
Where no #current value is specified this will simply add a skill, and where no #replace is set, then this will just remove the #current.
Working Demo on db<>fiddle
ADENDUM 2
For SQL Server 2016 that doesn't support STRING_AGG() you can use XML extensions as an alternative:
DECLARE #current VARCHAR(50) = 'UI',
#replace VARCHAR(50) = 'New Skill'
UPDATE t
SET t.key_skill = STUFF(s.NewList.value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM #T AS t
CROSS APPLY
( SELECT CONCAT(';', Value)
FROM ( SELECT Value
FROM STRING_SPLIT(t.key_skill, ';') AS s
WHERE s.value <> #current
UNION
SELECT #replace
WHERE #replace <> ''
) AS s
FOR XML PATH(''), TYPE
) AS s (NewList);

Related

How do I identify the column(s) responsible for “String or binary data would be truncated.”

I have an INSERT statement which looks like this:
INSERT INTO CLIENT_TABLE
SELECT NAME, SURNAME, AGE FROM CONTACT_TABLE
My example above is a basic one, but is there a way to pass in a SELECT statement and then check the returned column values against what the actual field sizes are?
Checking LEN against every column isnt practical. I am looking for something that is automated.
My debugging in that kind of problem is..
I am removing columns in the SELECT one by one, if did not return error, then you know what column is the cause of truncation problem.. but here are some tips on debugging.
Option 1: Start first with the columns that hold more character.. like VARCHAR, for example in your case, i think the column NAME, SURNAME are the one causes an error since AGE column does not hold many characters because its integer. You should debug something like that.
Option 2: You can investigate the column in your final output. The final SELECT will return all columns and its values, then you can counter check if the values matches what you input on the UI etc.
Ex. See the Expected vs. Actual Output result on the image below
Expected:
Actual Output:
My example in option 2 shows that the truncated string is the SURNAME as you can see..
NOTE: You can only use the Option 2 if the query did not return execution error, meaning to say that the truncated string did not return an error BUT created an unexpected split string which we don't want.
IF the query return an error, your best choice is Option 1, which consume more time but worth it, because that is the best way to make sure you found the exact column that causes the truncation problem
Then if you already found the columns that causes the problem, you can now adjust the size of the column or another way is to limit the input of the user ?, you can put some validation to users to avoid truncation problem, but it is all up to you on how you want the program works depending on your requirement.
My answers/suggestion is base on my experience in that kind of situation.
Hope this answer will help you. :)
Check max length for each field, this way you can identify the fields that are over char limit specified in your table e.g CLIENT_TABLE.
SELECT Max(Len(NAME)) MaxNamePossible
, Max(Len(SURNAME)) MaxSurNamePossible
, Max(Len(AGE)) MaxAgePossible
FROM CONTACT_TABLE
Compare the result with Client_Table Design
Like if in Client_Table "Name" is of Type Varchar(50) and validation query( written above) return more than 50 chars than "Name" field is causing over flow.
There is a great answer by Aaron Bertrand to the question:
Retrieve column definition for stored procedure result set
If you used SQL Server 2012+ you could use sys.dm_exec_describe_first_result_set. Here is a nice article with examples. But, even in SQL Server 2008 it is possible to retrieve the types of columns of the query. Aaron's answer explains it in details.
In fact, in your case it is easier, since you have a SELECT statement that you can copy-paste, not something that is hidden in a stored procedure. I assume that your SELECT is a complex query returning columns from many tables. If it was just one table you could use sys.columns with that table directly.
So, create an empty #tmp1 table based on your complex SELECT:
SELECT TOP(0)
NAME, SURNAME, AGE
INTO #tmp1
FROM CONTACT_TABLE;
Create a second #tmp2 table based on the destination of your complex SELECT:
SELECT TOP(0)
NAME, SURNAME, AGE
INTO #tmp2
FROM CLIENT_TABLE;
Note, that we don't need any rows, only columns for metadata, so TOP(0) is handy.
Once those #tmp tables exist, we can query their metadata using sys.columns and compare it:
WITH
CTE1
AS
(
SELECT
c.name AS ColumnName
,t.name AS TypeName
,c.max_length
,c.[precision]
,c.scale
FROM
tempdb.sys.columns AS c
INNER JOIN tempdb.sys.types AS t ON
c.system_type_id = t.system_type_id
AND c.user_type_id = t.user_type_id
WHERE
c.[object_id] = OBJECT_ID('tempdb.dbo.#tmp1')
)
,CTE2
AS
(
SELECT
c.name AS ColumnName
,t.name AS TypeName
,c.max_length
,c.[precision]
,c.scale
FROM
tempdb.sys.columns AS c
INNER JOIN tempdb.sys.types AS t ON
c.system_type_id = t.system_type_id
AND c.user_type_id = t.user_type_id
WHERE
c.[object_id] = OBJECT_ID('tempdb.dbo.#tmp2')
)
SELECT *
FROM
CTE1
FULL JOIN CTE2 ON CTE1.ColumnName = CTE2.ColumnName
WHERE
CTE1.TypeName <> CTE2.TypeName
OR CTE1.max_length <> CTE2.max_length
OR CTE1.[precision] <> CTE2.[precision]
OR CTE1.scale <> CTE2.scale
;
Another possible way to compare:
WITH
... as above ...
SELECT * FROM CTE1
EXCEPT
SELECT * FROM CTE2
;
Finally
DROP TABLE #tmp1;
DROP TABLE #tmp2;
You can tweak the comparison to suit your needs.
A manual solution is very quick if you are using SQL Server Manager Studio (SSMS). First capture the table structure of your SELECT statement into a working table:
SELECT COL1, COL2, ... COL99 INTO dbo.zz_CONTACT_TABLE
FROM CONTACT_TABLE WHERE 1=0;
Then in SSMS, right-click your original destination table (CLIENT_TABLE) and script it as create to a new SSMS window. Then right-click your working table (zz_CONTACT_TABLE) and script the creation of this table to a second SSMS window. Arrange both windows side by side and check the columns of zz_CONTACT_TABLE against CLIENT_TABLE. Differences in length and out-of-order columns will be immediately seen, even if there are hundreds of output columns.
Finally drop your working table:
DROP TABLE dbo.zz_CONTACT_TABLE;
Regarding an automated solution, it is difficult to see how this could work. Basically you are comparing a destination table (or a subset of columns in a destination table) against the output of a SELECT statement. I suppose you could write a stored procedure that takes two varchar parameters: the name of the destination table and the SELECT statement that would populate it. But this would not handle the case where only some columns of the destination are populated, and it would be more work than the manual solution above.
Here is some code to compare two row producing SQL statements to compare the columns. It takes as parameters two row-sets specified with server name, database name, and T-SQL query. It can compare data in different databases and even on different SQL Servers.
--setup parameters
declare #Server1 as varchar(128)
declare #Database1 as varchar(128)
declare #Query1 as varchar(max)
declare #Server2 as varchar(128)
declare #Database2 as varchar(128)
declare #Query2 as varchar(max)
set #Server1 = '(local)'
set #Database1 = 'MyDatabase'
set #Query1 = 'select * from MyTable' --use a select
set #Server2 = '(local)'
set #Database2 = 'MyDatabase2'
set #Query2 = 'exec MyTestProcedure....' --or use a procedure
--calculate statement column differences
declare #SQLStatement1 as varchar(max)
declare #SQLStatement2 as varchar(max)
set #Server1 = replace(#Server1,'''','''''')
set #Database1 = replace(#Database1,'''','''''')
set #Query1 = replace(#Query1,'''','''''')
set #Server2 = replace(#Server2,'''','''''')
set #Database2 = replace(#Database2,'''','''''')
set #Query2 = replace(#Query2,'''','''''')
CREATE TABLE #Qry1Columns(
[colorder] [smallint] NULL,
[ColumnName] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[TypeName] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[prec] [smallint] NULL,
[scale] [int] NULL,
[isnullable] [int] NULL,
[collation] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]
CREATE TABLE #Qry2Columns(
[colorder] [smallint] NULL,
[ColumnName] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[TypeName] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[prec] [smallint] NULL,
[scale] [int] NULL,
[isnullable] [int] NULL,
[collation] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]
set #SQLStatement1 =
'SELECT *
INTO #Qry1
FROM OPENROWSET(''SQLNCLI'',
''server=' + #Server1 + ';database=' + #Database1 + ';trusted_connection=yes'',
''select top 0 * from (' + #Query1 + ') qry'')
select colorder, syscolumns.name ColumnName, systypes.name TypeName, syscolumns.prec, syscolumns.scale, syscolumns.isnullable, syscolumns.collation
from tempdb.dbo.syscolumns
join tempdb.dbo.systypes
on syscolumns.xtype = systypes.xtype
where id = OBJECT_ID(''tempdb.dbo.#Qry1'')
order by 1'
insert into #Qry1Columns
exec(#SQLStatement1)
set #SQLStatement2 =
'SELECT *
INTO #Qry1
FROM OPENROWSET(''SQLNCLI'',
''server=' + #Server2 + ';database=' + #Database2 + ';trusted_connection=yes'',
''select top 0 * from (' + #Query2 + ') qry'')
select colorder, syscolumns.name ColumnName, systypes.name TypeName, syscolumns.prec, syscolumns.scale, syscolumns.isnullable, syscolumns.collation
from tempdb.dbo.syscolumns
join tempdb.dbo.systypes
on syscolumns.xtype = systypes.xtype
where id = OBJECT_ID(''tempdb.dbo.#Qry1'')
order by 1'
insert into #Qry2Columns
exec(#SQLStatement2)
select ISNULL( #Qry1Columns.colorder, #Qry2Columns.colorder) ColumnNumber,
#Qry1Columns.ColumnName ColumnName1,
#Qry1Columns.TypeName TypeName1,
#Qry1Columns.prec prec1,
#Qry1Columns.scale scale1,
#Qry1Columns.isnullable isnullable1,
#Qry1Columns.collation collation1,
#Qry2Columns.ColumnName ColumnName2,
#Qry2Columns.TypeName TypeName2,
#Qry2Columns.prec prec2,
#Qry2Columns.scale scale2,
#Qry1Columns.isnullable isnullable2,
#Qry2Columns.collation collation2
from #Qry1Columns
join #Qry2Columns
on #Qry1Columns.colorder=#Qry2Columns.colorder
You can tweak the finally select statement to highlight any differences that you wish. You can also wrap this up in a procedure and make a nice little user interface for it if you like, so that it's literally a cut and paste away to quick results.

SQL How to Split One Column into Multiple Variable Columns

I am working on MSSQL, trying to split one string column into multiple columns. The string column has numbers separated by semicolons, like:
190230943204;190234443204;
However, some rows have more numbers than others, so in the database you can have
190230943204;190234443204;
121340944534;340212343204;134530943204
I've seen some solutions for splitting one column into a specific number of columns, but not variable columns. The columns that have less data (2 series of strings separated by commas instead of 3) will have nulls in the third place.
Ideas? Let me know if I must clarify anything.
Splitting this data into separate columns is a very good start (coma-separated values are an heresy). However, a "variable number of properties" should typically be modeled as a one-to-many relationship.
CREATE TABLE main_entity (
id INT PRIMARY KEY,
other_fields INT
);
CREATE TABLE entity_properties (
main_entity_id INT PRIMARY KEY,
property_value INT,
FOREIGN KEY (main_entity_id) REFERENCES main_entity(id)
);
entity_properties.main_entity_id is a foreign key to main_entity.id.
Congratulations, you are on the right path, this is called normalisation. You are about to reach the First Normal Form.
Beweare, however, these properties should have a sensibly similar nature (ie. all phone numbers, or addresses, etc.). Do not to fall into the dark side (a.k.a. the Entity-Attribute-Value anti-pattern), and be tempted to throw all properties into the same table. If you can identify several types of attributes, store each type in a separate table.
If these are all fixed length strings (as in the question), then you can do the work fairly simply (at least relative to other solutions):
select substring(col, 1+13*(n-1), 12) as val
from t join
(select 1 as n union all select union all select 3
) n
on len(t.col) <= 13*n.n
This is a useful hack if all the entries are the same size (not so easy if they are of different sizes). Do, however, think about the data structure because semi-colon (or comma) separated list is not a very good data structure.
IF I were you, I would create a simple function that is dividing values separated with ';' like this:
IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N'fn_Split_List') AND xtype IN (N'FN', N'IF', N'TF'))
BEGIN
DROP FUNCTION [dbo].[fn_Split_List]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[fn_Split_List](#List NVARCHAR(512))
RETURNS #ResultRowset TABLE ( [Value] NVARCHAR(128) PRIMARY KEY)
AS
BEGIN
DECLARE #XML xml = N'<r><![CDATA[' + REPLACE(#List, ';', ']]></r><r><![CDATA[') + ']]></r>'
INSERT INTO #ResultRowset ([Value])
SELECT DISTINCT RTRIM(LTRIM(Tbl.Col.value('.', 'NVARCHAR(128)')))
FROM #xml.nodes('//r') Tbl(Col)
RETURN
END
GO
Than simply called in this way:
SET NOCOUNT ON
GO
DECLARE #RawData TABLE( [Value] NVARCHAR(256))
INSERT INTO #RawData ([Value] )
VALUES ('1111111;22222222')
,('3333333;113113131')
,('776767676')
,('89332131;313131312;54545353')
SELECT SL.[Value]
FROM #RawData AS RD
CROSS APPLY [fn_Split_List] ([Value]) as SL
SET NOCOUNT OFF
GO
The result is as the follow:
Value
1111111
22222222
113113131
3333333
776767676
313131312
54545353
89332131
Anyway, the logic in the function is not complicated, so you can easily put it anywhere you need.
Note: There is not limitations of how many values you will have separated with ';', but there are length limitation in the function that you can set to NVARCHAR(MAX) if you need.
EDIT:
As I can see, there are some rows in your example that will caused the function to return empty strings. For example:
number;number;
will return:
number
number
'' (empty string)
To clear them, just add the following where clause to the statement above like this:
SELECT SL.[Value]
FROM #RawData AS RD
CROSS APPLY [fn_Split_List] ([Value]) as SL
WHERE LEN(SL.[Value]) > 0

Solutions for tracking how many times certain parameters are used in a stored procedure SQL Server 2008

In my database have tables with structures similar to:
create table ItemNameSearches
(ItemName varchar(50) not null,timesSearched int not null,
primary key(ItemName))
and
create table ItemList
(ItemName varchar(50),
primary key (ItemName))
My idea is to have people enter in through a webform a comma-separated list of values so that they can get information about certain items. The table ItemList contains information about the item for which they searched (although the table structure doesn't reflect that in this example). If, however, the item searched for doesn't appear in the ItemList table, I would like for that ItemName to be inserted into the ItemNameSearches so I could have a better idea of what people are searching for.
Scenario 1: an item is searched for the first time and a new row is inserted into the ItemNameSearches table. I'm pretty sure this is an area for triggers, but I'm not familiar with using them so I wrote the following stored procedure:
create proc spSearchItemName
#itemName1 varchar(50)
,#itemName2 varchar(50) = null
,#itemName3 varchar(50) = null
,#itemName4 varchar(50) = null
as
begin
;with searchList
as
(select x.itemName
from (values (#itemName1)
,(#itemName2)
,(#itemName3)
,(#itemName4)
) as x(itemName)
where x.itemName is not null
--these are optional parameters just to give the user more flexibility
--on if they want to look at multiple items at once or not
)
insert into ItemNameSearches(itemName,timesSearched)
values
(
(select sl.itemName
from searchList as sl
left outer join ItemList as il
on il.itemName=sl.itemName
where il.itemName is null
--this subquery finds the items searched for that are not present in the
--itemlist table and sets timesSearched =1 for each
),1
)
end
This is well and good for the items that are searched for that do not appear in the ItemList table, but I would have to do something like the following procedure if they DID search for an item that was in the ItemList table
;with searchList
as
(select x.itemName
from (values ('item 1')
,('item 2')
,('item 3')
,('item 5')
) as x(itemName)
)
update ins
set timesSearched = timesSearched +1
from ItemNameSearches as ins
where itemName in
(select itemName from searchList)
So this will add 1 to the number of times an item was searched for if it exists in the ItemList table. Can someone provide a neat manner of how to solve these two different situations? Is this something that is a good candidate for triggers?
Thanks to #Gordon Linoff for providing the direction of using the merge statement, it ended up working perfectly for what I wanted to do. I ended up using the following sproc and it works fine.
alter proc spSearchDrugName
#drugName1 varchar(50)
,#drugName2 varchar(50) = null
,#drugName3 varchar(50) = null
,#drugName4 varchar(50) = null
,#drugName5 varchar(50) = null
as
begin
declare #searchList table(drugName varchar(50))
insert into #searchList
values (#drugName1)
,(#drugName2)
,(#drugName3)
,(#drugName4)
,(#drugName5)
merge DrugListSearches as d
using(select drugName from #searchList where drugName is not null) as s
on s.drugName = d.drugName
when matched then
update set d.timesSearched = d.timesSearched + 1
when not matched then
insert (drugname,timesSearched) values (s.drugName,1);
end

SQL How to find if all values from one field exist in another field in any order

I am trying to match data from an external source to an in house source. For example one table would have a field with a value of "black blue" and another table would have a field with a value of "blue black". I am trying to figure out how to check if all individual words in the first table are contained in a record the 2nd table in any order. It's not always two words that need to be compared it could be 3 or 4 as well. I know I could use a cursor and build dynamic sql substituting the space with the AND keywod and using the contains function but I'm hoping not to have to do that.
Any help would be much appreciated.
Try doing something like this: Split the data from the first table on the space into a temporary table variable. Then use CHARINDEX to determine if each word is contained in the second table's record. Then just do this for each word in the first record and if the count is the same as the successful checks then you know every word from the first record is used in the second.
Edit: Use a Split function such as:
CREATE FUNCTION dbo.Split (#sep char(1), #s varchar(512))
RETURNS table
AS
RETURN (
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(#sep, #s)
UNION ALL
SELECT pn + 1, stop + 1, CHARINDEX(#sep, #s, stop + 1)
FROM Pieces
WHERE stop > 0
)
SELECT pn,
SUBSTRING(#s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s
FROM Pieces
)
Here's another method you could try, you could sample some simple attributes of your strings such as, length, number of spaces, etc.; then you could use a cross-join to create all of the possible string match combinations.
Then within your where-clause you can sort by matches, the final piece of which in this example is a check using the patindex() function to see if the sampled piece of the first string is in the second string.
-- begin sample table variable set up
declare #s table(
id int identity(1,1)
,string varchar(255)
,numSpace int
,numWord int
,lenString int
,firstPatt varchar(255)
);
declare #t table(
id int identity(1,1)
,string varchar(255)
,numSpace int
,numWord int
,lenString int
);
insert into #t(string)
values ('my name');
insert into #t(string)
values ('your name');
insert into #t(string)
values ('run and jump');
insert into #t(string)
values ('hello my name is');
insert into #s(string)
values ('name my');
insert into #s(string)
values ('name your');
insert into #s(string)
values ('jump and run');
insert into #s(string)
values ('my name is hello');
update #s
set numSpace = len(string)-len(replace(string,' ',''));
update #s
set numWord = len(string)-len(replace(string,' ',''))+1;
update #s
set lenString = len(string);
update #s
set firstPatt = rtrim(substring(string,1,charindex(' ',string,0)));
update #t
set numSpace = len(string)-len(replace(string,' ',''));
update #t
set numWord = len(string)-len(replace(string,' ',''))+1;
update #t
set lenString = len(string);
-- end sample table variable set up
-- select all combinations of strings using a cross join
-- and sort the entries in your where clause
-- the pattern index checks to see if the sampled string
-- from the first table variable is in the second table variable
select *
from
#s s cross join #t t
where
s.numSpace = t.numspace
and s.numWord = t.numWord
and s.lenString = t.lenString
and patindex('%'+s.firstPatt+'%',t.string)>0;

Column name or number of supplied values does not match table definition

In the SQL Server, I am trying to insert values from one table to another by using the below query:
delete from tblTable1
insert into tblTable1 select * from tblTable1_Link
I am getting the following error:
Column name or number of supplied values does not match table definition.
I am sure that both the tables have the same structure, same column names and same data types.
They don't have the same structure... I can guarantee they are different
I know you've already created it... There is already an object named ‘tbltable1’ in the database
What you may want is this (which also fixes your other issue):
Drop table tblTable1
select * into tblTable1 from tblTable1_Link
I want to also mention that if you have something like
insert into blah
select * from blah2
and blah and blah2 are identical keep in mind that a computed column will throw this same error...
I just realized that when the above failed and I tried
insert into blah (cola, colb, colc)
select cola, colb, colc from blah2
In my example it was fullname field (computed from first and last, etc)
for inserts it is always better to specify the column names see the following
DECLARE #Table TABLE(
Val1 VARCHAR(MAX)
)
INSERT INTO #Table SELECT '1'
works fine, changing the table def to causes the error
DECLARE #Table TABLE(
Val1 VARCHAR(MAX),
Val2 VARCHAR(MAX)
)
INSERT INTO #Table SELECT '1'
Msg 213, Level 16, State 1, Line 6
Insert Error: Column name or number of
supplied values does not match table
definition.
But changing the above to
DECLARE #Table TABLE(
Val1 VARCHAR(MAX),
Val2 VARCHAR(MAX)
)
INSERT INTO #Table (Val1) SELECT '1'
works. You need to be more specific with the columns specified
supply the structures and we can have a look
The problem is that you are trying to insert data into the database without using columns. SQL server gives you that error message.
Error: insert into users values('1', '2','3') - this works fine as long you only have 3 columns
If you have 4 columns but only want to insert into 3 of them
Correct: insert into users (firstName,lastName,city) values ('Tom', 'Jones', 'Miami')
Beware of triggers. Maybe the issue is with some operation in the trigger for inserted rows.
Dropping the table was not an option for me, since I'm keeping a running log. If every time I needed to insert I had to drop, the table would be meaningless.
My error was because I had a couple columns in the create table statement that were products of other columns, changing these fixed my problem. eg
create table foo (
field1 as int
,field2 as int
,field12 as field1 + field2 )
create table copyOfFoo (
field1 as int
,field2 as int
,field12 as field1 + field2) --this is the problem, should just be 'as int'
insert into copyOfFoo
SELECT * FROM foo
The computed columns make the problem.
Do not use SELECT *. You must specify each fields after SELECT except computed fields
some sources for this issues are as below
1- Identity column ,
2- Calculated Column
3- different structure
so check those 3 , i found my issue was the second one ,
For me the culprit is int value assigned to salary
Insert into Employees(ID,FirstName,LastName,Gender,Salary) values(3,'Canada', 'pa', 'm',15,000)
in salary column When we assign 15,000 the compiler understand 15 and 000.
This correction works fine for me.
Insert into Employees(ID,FirstName,LastName,Gender,Salary) values(4,'US', 'sam', 'm',15000)
Update to SQL server 2016/2017/…
We have some stored procedures in place to import and export databases.
In the sp we use (amongst other things) RESTORE FILELISTONLY FROM DISK where we create a
table "#restoretemp" for the restore from file.
With SQL server 2016, MS has added a field SnapshotURL nvarchar(360) (restore url Azure) what has caused the error message.
After I have enhanced the additional field, the restore has worked again.
Code snipped (see last field):
SET #query = 'RESTORE FILELISTONLY FROM DISK = ' + QUOTENAME(#BackupFile , '''')
CREATE TABLE #restoretemp
(
LogicalName nvarchar(128)
,PhysicalName nvarchar(128)
,[Type] char(1)
,FileGroupName nvarchar(128)
,[Size] numeric(20,0)
,[MaxSize] numeric(20,0)
,FileID bigint
,CreateLSN numeric(25,0)
,DropLSN numeric(25,0) NULL
,UniqueID uniqueidentifier
,ReadOnlyLSN numeric(25,0)
,ReadWriteLSN numeric(25,0)
,BackupSizeInByte bigint
,SourceBlockSize int
,FilegroupID int
,LogGroupGUID uniqueidentifier NULL
,DifferentialBaseLSN numeric(25,0)
,DifferentialbaseGUID uniqueidentifier
,IsReadOnly bit
,IsPresent bit
,TDEThumbprint varbinary(32)
-- Added field 01.10.2018 needed from SQL Server 2016 (Azure URL)
,SnapshotURL nvarchar(360)
)
INSERT #restoretemp EXEC (#query)
SET #errorstat = ##ERROR
if #errorstat <> 0
Begin
if #Rueckgabe = 0 SET #Rueckgabe = 6
End
Print #Rueckgabe
Check your id. Is it Identity? If it is then make sure it is declared as ID not null Identity(1,1)
And before creating your table , Drop table and then create table.
The problem I had that caused this error was that I was trying to insert null values into a NOT NULL column.
I had the same problem, and the way I worked around it is probably not the best but it is working now.
It involves creating a linked server and using dynamic sql - not the best, but if anyone can suggest something better, please comment/answer.
declare #sql nvarchar(max)
DECLARE #DB_SPACE TABLE (
[DatabaseName] NVARCHAR(128) NOT NULL,
[FILEID] [smallint] NOT NULL,
[FILE_SIZE_MB] INT NOT NULL DEFAULT (0),
[SPACE_USED_MB] INT NULL DEFAULT (0),
[FREE_SPACE_MB] INT NULL DEFAULT (0),
[LOGICALNAME] SYSNAME NOT NULL,
[DRIVE] NCHAR(1) NOT NULL,
[FILENAME] NVARCHAR(260) NOT NULL,
[FILE_TYPE] NVARCHAR(260) NOT NULL,
[THE_AUTOGROWTH_IN_KB] INT NOT NULL DEFAULT(0)
,filegroup VARCHAR(128)
,maxsize VARCHAR(25)
PRIMARY KEY CLUSTERED ([DatabaseName] ,[FILEID] )
)
SELECT #SQL ='SELECT [DatabaseName],
[FILEID],
[FILE_SIZE_MB],
[SPACE_USED_MB],
[FREE_SPACE_MB],
[LOGICALNAME],
[DRIVE],
[FILENAME],
[FILE_TYPE],
[THE_AUTOGROWTH_IN_KB]
,filegroup
,maxsize FROM OPENQUERY('+ QUOTENAME('THE_MONITOR') + ','''+ ' EXEC MASTER.DBO.monitoring_database_details ' +''')'
exec sp_executesql #sql
INSERT INTO #DB_SPACE(
[DatabaseName],
[FILEID],
[FILE_SIZE_MB],
[SPACE_USED_MB],
[FREE_SPACE_MB],
[LOGICALNAME],
[DRIVE],
[FILENAME],
[FILE_TYPE],
THE_AUTOGROWTH_IN_KB,
[filegroup],
maxsize
)
EXEC SP_EXECUTESQL #SQL
This is working for me now.
I can guarantee the number of columns and type of columns returned by the stored procedure are the same as in this table, simply because I return the same table from the stored procedure.
In my case, I had:
insert into table1 one
select * from same_schema_as_table1 same_schema
left join...
and I had to change select * to select same_schema.*.
You're missing column name after TableName in insert query:
INSERT INTO TableName**(Col_1,Col_2,Col_3)** VALUES(val_1,val_2,val_3)
In my case the problem was that the SP I was executing returned two result sets, and only the second result set was matching the table definition.