string or binary data would be truncated. in sql server - sql

How to overcome this?
I have insert statements for large tables. there were lot of columns in those tables.
Is there any simple way to find the column which is causing above error?

The simplest way is to increase the length of the column which is giving you the error "string or binary data would be truncated. in sql server".
Now to find which column is giving that error you can check How to find what column caused the String or binary data would be truncated message
First thing we need to know is what the columns are in the table that are (n)chars or (n)varchar
select column_name
from information_schema.columns
where table_name = 'temp'
and data_type in('varchar','char','nvarchar','nchar')
Output:
column_name
————–
Col1
Col2
Col3
Col4
Col5
That was easy, now we want to know the max length of the data in each column
declare #sql varchar(8000)
select #sql = 'select 0 as _col0 ,'
select #sql += 'max(len( ' + column_name+ ')) AS ' + column_name + ','
from information_schema.columns
where table_name = 'temp'
and data_type in('varchar','char','nvarchar','nchar')
select #sql = left(#sql,len(#sql) -1)
select #sql +=' into MaxLengths from temp'
--select #sql -debugging so simple, a caveman can do it
exec (#sql)
That code basically creates and runs the following
select 0 as _col0 ,
max(len( Col1)) AS Col1,
max(len( Col2)) AS Col2,
max(len( Col3)) AS Col3,
max(len( Col4)) AS Col4,
max(len( Col5)) AS Col5
into MaxLengths
from temp
If we now look in the MaxLengths table we will see the following
select * from MaxLengths
_col0 Col1 Col2 Col3 Col4 Col5
---------------------------------------------------
0 13 20 6 4 15
Next to figure out is what the max length of the column itself is in the table that we want to insert into
Run the following query
select character_maximum_length,column_name
from information_schema.columns
where table_name = 'TestTrunc'
and data_type in('varchar','char','nvarchar','nchar')
Result
character_maximum_length column_name
--------------------------------------------
10 Col1
15 Col2
20 Col3
3 Col4
10 Col5
We will again do this dynamically and insert the values into another table
declare #sql varchar(8000)
select #sql = 'select 0 as _col0, '
select #sql += '' + convert(varchar(20),character_maximum_length)+ ' AS ' + column_name + ','
from information_schema.columns
where table_name = 'TestTrunc'
and data_type in('varchar','char','nvarchar','nchar')
select #sql = left(#sql,len(#sql) -1)
select #sql +=' into TempTrunc '
--select #sql -debugging so simple, a caveman can do it
exec (#sql)
Now we can see what we have in the two tables
select 'TempTrunc' as TableNAme,* from TempTrunc
union all
select 'MaxLengths' as TableNAme,* from MaxLengths
TableNAme _col0 Col1 Col2 Col3 Col4 Col5
-------------------------------------------------------------
TempTrunc 0 10 15 20 3 10
MaxLengths 0 13 20 6 4 15
As you can see, all columns except for Col3 will cause the truncation problem
Of course we want to do something like this, it will tell us which columns have truncation problems
select case when t.col1 > tt.col1 then 'truncation' else 'no truncation' end as Col1,
case when t.col2 > tt.col2 then 'truncation' else 'no truncation' end as Col2,
case when t.col3 > tt.col3 then 'truncation' else 'no truncation' end as Col3,
case when t.col4 > tt.col4 then 'truncation' else 'no truncation' end as Col4,
case when t.col5 > tt.col5 then 'truncation' else 'no truncation' end as Col5
from MaxLengths t
join TempTrunc tt on t._col0 = tt._col0
Col1 Col2 Col3 Col4 Col5
------------------------------------------------------------------------------------
truncation truncation no truncation truncation truncation
Source

I believe you already know that this is related with inserting a string value into a column with less size
You can query table columns from know table names and compare them.
select
OBJECT_NAME(object_id),
name,
max_length,
precision,
scale
from sys.columns where object_id in (
select object_id from sys.tables where name in ('tbl','Emp')
)

Related

MSSQL select lowest but not NULL/zero value from 25 columns

I have 25 (numeric) columns in one table in MSSQL and I need select lowest value, but not NULL or 0 value.
Columns are named like "%_price" (aaa_price, bbb_price, ccc_price...).
Some columns contains 0 or NULL value.
Example:
table (aaa_price, bbb_price, ccc_price, ddd_price, eee_price, fff_price)
value (NULL, 0, 324.23, 162.50, NULL, 1729.72 )
Right result:
162.50
I can use some "brute force" method like:
SELECT CASE
WHEN Col1 <= Col2 AND Col1 <= Col3 AND Col1 <= Col4 AND Col1 <= Col5 THEN Col1
WHEN Col2 <= Col3 AND Col2 <= Col4 AND Col2 <= Col5 THEN Col2
WHEN Col3 <= Col4 AND Col3 <= Col5 THEN Col3
WHEN Col4 <= Col5 THEN Col4
ELSE Col5
END AS [Min Value] FROM [Your Table]
But its insane with 25 columns... is there any better solution?
Thank You!
Cross apply can be good option in this case:
select
*
from
myTable
cross apply (select
minVal = min(val)
from (
values (aaa_price),(bbb_price),(...)
) t(val) where val > 0
) q
Edit:
You have to use dynamic SQL if you want to get column names from INFORMATION_SCHEMA.COLUMNS table.
declare #sql varchar(8000)
declare #cols varchar(8000)
select #cols =
stuff((
SELECT
',(' + COLUMN_NAME + ')'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = 'mytable'
AND TABLE_SCHEMA='dbo'
AND COLUMN_NAME LIKE '%price'
for xml path('')
), 1, 1, '')
set #sql = 'select
*
from
mytable
cross apply (select
minVal = min(val)
from (
values ' + #cols + '
) t(val) where val > 0
) q'
exec (#sql)
You can create a dynamic SQL statement and execute it in the following form
declare #tablename sysname = 'MultipleNumericColumns'
declare #sql nvarchar(max)
select #sql = isnull(#sql + ' union all ','') + '
SELECT ' + name + ' as colname from ' + #tablename
from sys.all_columns
where
object_id = OBJECT_ID(#tablename)
set #sql = '
select min(colname)
from (
' + #sql + '
) t
where colname > 0'
EXECUTE(#sql)
You can realize that first I get the column names from system view
You can exclude columns that you don't want or use a pattern like name like '%price% etc at this step
Then I build a dynamic SQL query into a string variable as sql command
Please note that I use WHERE clause for greater than 0, etc
Final step is execution with EXECUTE command
Use UNPIVOT
SELECT Min(VALUE)
FROM (
SELECT Col1, Col2, Col3...
FROM YourTable
) t
UNPIVOT (VALUE FOR ITEM IN (Col1, Col2, Col3...)) u
WHERE VALUE != 0

SQL Server : Reuse calculated variable in select clause

I have the following table structure:
col1 col2 col3 col4
-------------------------------
aK Mbcd ABc defgh
col2, col3 and col4 columns are of type varchar(100) and col1 has type varchar(500).
I need a select query to have the output as following
col1 col2 col3 col4
-------------------------------
aK,Mb cd,A Bc,d efgh
Logic is explained as mentioned below:
In the result, Col2, col3 and col4 can have maximum 4 characters but col1 can have more than 4 characters upto 100.
If any column has more characters, last 4 characters will be retained in the same column and other extra columns will be concatenated with previous column's value separated by comma , and the same rule will be applied on the concatenated values as well.
I've written the following T-SQL statement. It works fine for last two columns. But I want to use new calculated value of col3 to strip out extra characters after adding some from col4
SELECT
CASE
WHEN X.Col4Length > 4
THEN concat(X.col3, ',', substring(x.col4, 0, X.Col4Length - 3))
ELSE X.col3
END AS col3,
CASE
WHEN X.Col4Length > 4
THEN substring(x.col4, X.Col4Length - 3, x.Col4Length)
ELSE X.col4
END AS col4
FROM
(SELECT
Col1, Col2, Col3, Col4,
Len(Col1) AS Col1Length,
Len(Col2) AS Col2Length,
Len(Col3) AS Col3Length,
Len(Col4) AS Col4Length
FROM
mytable) X
My try with a simple sub-query
with t1 as (
select 'aK' col1, 'Mbcd' col2, 'ABc' col3, 'defgh' col4
---
SELECT LEFT(col, LEN(col) - 12) col1,
RIGHT(LEFT(col, LEN(col) - 8), 4) col2,
RIGHT(LEFT(col, LEN(col) - 4), 4) col3,
RIGHT(col, 4) AS col4
FROM
(
SELECT col1+','+col2+','+col3+','+col4 AS col
FROM t1
) t;
You want to reuse calculated variables
There are two set-based /inline / adhoc approaches (and many more ugly procedural):
CTEs to do this for the whole set in advance
CROSS APPLY for the same on row level
Try it like this (CTE approach)
DECLARE #tbl TABLE(col1 VARCHAR(100),col2 VARCHAR(100),col3 VARCHAR(100),col4 VARCHAR(100));
INSERT INTO #tbl VALUES
('aK','Mbcd','ABc','defgh')
,('123456','abc','3456','123456789');
WITH ResolveCol4 AS
(
SELECT *
,RIGHT(col4,4) AS Col4_resolved
,col3 + ',' + CASE WHEN LEN(col4)>4 THEN SUBSTRING(col4,1,LEN(col4)-4) ELSE '' END AS col3_New
FROM #tbl
)
,ResolveCol3 AS
(
SELECT *
,RIGHT(col3_New,4) AS Col3_resolved
,col2 + ',' + CASE WHEN LEN(col3_New)>4 THEN SUBSTRING(col3_New,1,LEN(col3_New)-4) ELSE '' END AS col2_New
FROM ResolveCol4
)
,ResolveCol2 AS
(
SELECT *
,RIGHT(col2_New,4) AS Col2_resolved
,col1 + ',' + CASE WHEN LEN(col2_New)>4 THEN SUBSTRING(col2_New,1,LEN(col2_New)-4) ELSE '' END AS col1_New
FROM ResolveCol3
)
SELECT col1_new,Col2_resolved,Col3_resolved,Col4_resolved
FROM ResolveCol2
The result
aK,Mb cd,A Bc,d efgh
123456,abc,34 56,1 2345 6789

Remove a ROW of NULL entries SQl Server

If I have a table which looks like this
Is there a way to remove all rows in a SQL tablw without knowing the column titles, if each item in that row is NULL?
Thanks!
Maybe there is another way but since you've tagged SqlCommand i would use ADO.NET. You can use dataReader.GetSchemaTable to retrieve column informations only:
// retrieve column names only
DataTable schema = null;
using (var con = new SqlConnection(Properties.Settings.Default.ConnectionString))
{
using (var schemaCommand = new SqlCommand("SELECT * FROM dbo.TableName", con))
{
con.Open();
using (var reader = schemaCommand.ExecuteReader(CommandBehavior.SchemaOnly))
{
schema = reader.GetSchemaTable();
}
}
}
// build sql with parameters dynamically
var columnQuery = schema.AsEnumerable()
.Select(r => string.Format("{0} IS NULL", r.Field<String>("ColumnName")));
string where = string.Join(" AND ", columnQuery);
string sql = string.Format("DELETE FROM dbo.TableName WHERE {0}", where);
// here you are
Of course you need two queries, one to select the columns and one to delete the null-rows. So the best way would be to know the columns in the first place.
Here's a snippet that should allow you to first SEE what your delete statements will look like, and then run it again when you're ready to actually perform the deletions:
begin
set nocount on
create table #temp_sql_text (sql_text varchar(max))
declare #table_name varchar(max)
declare c cursor for
select name from sys.tables order by name
open c
fetch next from c into #table_name
while ##fetch_status = 0
begin
insert into #temp_sql_text
select 'delete from ' + #table_name + ' where ' + replace(stuff(ColumnList, 1, 3, ''), '|||', ' is null and ') + ' is null'
from (
select '|||' + '[' + c.name + ']'
from sys.tables t inner join sys.columns c on c.object_id = t.object_id
where t.name = #table_name
order by c.column_id
for xml path('')
) o (ColumnList)
fetch next from c into #table_name
end
close c
deallocate c
declare #sql_text varchar(max)
declare c2 cursor for
select sql_text from #temp_sql_text
open c2
fetch next from c2 into #sql_text
while ##fetch_status = 0
begin
--Suggest just printing out the delete queries as a test before executing them!
print #sql_text
--Uncomment the next line to also execute the delete queries
--exec sp_executesql #sql_text
fetch next from c2 into #sql_text
end
close c2
deallocate c2
drop table #temp_sql_text
set nocount off
end
try this: I just tried on SQL server 2008 R2. I am not good with how to place the code in answer. please bare it.
declare #tempvar as nvarchar(max)
set #tempvar='X'
select #tempvar=#tempvar+COLUMN_NAME+' is NULL AND ' from
DatabaseName.INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='Table_Name'
print #tempvar
Select #tempvar=CONCAT(#tempvar, 'X')
print #tempvar
Select #tempvar= REPLACE(#tempvar,'AND X','')
print #tempvar
Select #tempvar= REPLACE(#tempvar,'X',' ')
print #tempvar
declare #queryex as nvarchar(max)
set #queryex='select * from Table_Name where'+#tempvar
print #queryex
EXEC sp_executesql #queryex
If you know the number of columns in the table, you can do this:
delete from
tableToScrub
where
checksum(*) =
checksum(
cast(null as int),
cast(null as int)
-- Add one null to checksum per column
)
If you don't know the number of columns, you can do this:
with
nullset
as (
select
cast(null as int) as col1,
cast(null as int) as col2,
cast(null as int) as col3,
cast(null as int) as col4,
cast(null as int) as col5,
cast(null as int) as col6,
cast(null as int) as col7
-- add columns as necessary for your tables
)
,
nullchecksums
as (
select checksum(col1) as nullsum, 1 as columnCount from nullset union
select checksum(col1, col2), 2 from nullset union
select checksum(col1, col2, col3), 3 from nullset union
select checksum(col1, col2, col3, col4) , 4 from nullset union
select checksum(col1, col2, col3, col4, col5) , 5 from nullset union
select checksum(col1, col2, col3, col4, col5, col6) , 6 from nullset union
select checksum(col1, col2, col3, col4, col5, col6, col7) , 7 from nullset
-- add rows as necessary
)
delete from
tableToScrub
where
checksum(*) = (
select
nullsum
from
nullchecksums
where
columncount = (
select
count(1) as columnCount
from
sys.columns
where
object_id = object_id('SchemaName.TableToScrub')
)
)

COUNT the number of columns where a condition is true? SQL Server 2008 R2

I have a table that looks something like
ID Col1 Col2 Col3 Col4
1 3 5 3 3
What I want to do is COUNT the number of 3s in this particular row.
I have tried the
select COUNT(*)
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'TableName' -- but obviously I need WHERE Col1 = 3 OR Col2 = 3...
What would be the best way to achieve this?
Based on what OP asked, this can be done
select
CASE WHEN Col1 = 3 then 1 ELSE 0 END +
CASE WHEN Col2 = 3 then 1 ELSE 0 END +
CASE WHEN Col3 = 3 then 1 ELSE 0 END +
CASE WHEN Col4 = 3 then 1 ELSE 0 END
From TableName
I don't really enjoy working with PIVOT so here a solution using APPLY.
SELECT
T.id
, Val
, COUNT(*)
FROM MyTable AS T
CROSS APPLY (
VALUES
(T.C1)
, (T.C2)
, (T.C3)
, (T.C4)
) AS X(Val)
GROUP BY T.Id, X.Val
ORDER BY T.Id, X.val
Please find the sample code:
DECLARE #Query VARCHAR(MAX) = 'SELECT Count = '
SELECT
#Query += '( CASE WHEN '+ COLUMN_NAME + ' = 3 THEN 1 ELSE 0 END ) + '
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'TEST' AND COLUMN_NAME <> 'ID'
SET #Query = SUBSTRING(#Query, 1, DATALENGTH(#Query) - 2) + ' FROM TEST WHERE ID = 1'
EXEC(#Query)

SQL Server: select value, split on a delimiter, then update two columns

I have a SQL Server 2008 database table with three varchar Columns - Col1, Col2, Col3. Col1 has data in it with a single space in between, Col2 and Col3 are empty.
I need to write a query to select the data from Col1, break up each value using the space as the delimiter, and inserting the data on either side of the space into Col2 and Col3 respectively.
I am not too sure how to proceed. Can this be accomplished in SQL, or should I create a small program to do the work for me? I'd appreciate a pointer in the right direction if this can be accomplished via SQL.
Thanks.
UPDATE table SET
Col2 = SUBSTRING(Col1, 1, CHARINDEX(' ', Col1)-1),
Col3 = SUBSTRING(Col1, CHARINDEX(' ', Col1)+1, 8000)
WHERE Col1 LIKE '% %';
If you can guarantee there is only one space:
create table #temp (col1 varchar(50),col2 varchar(50), col3 varchar(50))
insert into #temp (col1)
select 'test 1'
union all
select 'test 2'
union all
select 'test 3'
update #temp
set col2 = left(col1, charindex (' ', col1)),
col3 = substring(col1,charindex (' ', col1)+1, len(col1))
from #temp