I inherited some partially complete sql code that I can't get to work.
it accesses multiple databases, so it first searches for proper database using a userID number, then inserts that database name into a query. the part i'm having a problem with (extremely abbreviated) is...
DECLARE #sql AS VARCHAR(8000)
SET #sql = 'INSERT INTO ['+#DatabaseName+'].dbo.[customer]
( -- containing about 200 columns. )
VALUES(...)'
PRINT #sql
EXEC(#sql)
i would get errors in the middle of a column name, sometimes saying it's expecting a parenthesis or quote. i started deleting white space so that, ie, [first name],[last name] were on the same line and not two different lines and that would get me a little further down the query. i don't have much more white spaces i can delete and i'm only just getting into the Values(...) portion of it. the weird thing is. i copy and pasted just the columns portion and put it into Word and it comes up as being only about 3,000 characters, including white space.
am i missing something?
if it means anything, i'm running microsoft sql server 2005, and using the sql server management studio for editing
thanks!
See here: SQL Server: When 8000 Characters Is Not Enough for a couple of solutions
extremely abbreviated
Well, that doesn't really help since you have likely abbreviated away the cause of the issue.
If I were to guess, I have seen cases where NCHAR or CHAR variables/columns were involved. These expand to their full length when used in string concatenation and it will cause the final statement to be too long.
For what it's worth for style or otherwise, use NVarchar(Max) always for SQL Server 2005 and onwards. In fact, that is the expected type if you use sp_executesql.
If you check for fixed-width N/CHAR columns and switch to nvarchar(max), you may see the problem go away.
EDIT: Test showing NVarchar(Max) holding well in excess of 8000 bytes.
declare #sql nvarchar(max)
-- this CTE sets up the columns, 1 as field1, 2 as field2 etc
-- it creates 2000 columns
;with CTE(n, t) AS (
select 1, convert(nvarchar(max),'1 as field1')
union all
select n+1, convert(nvarchar(max),RIGHT(n, 12) + ' as field'+RIGHT(n, 12))
from cte
where N < 2000)
select #sql = coalesce(#sql+',','') + t
from CTE
option (maxrecursion 2000) -- needed, the default of 100 is not nearly enough
-- add the SELECT bit to make a proper SQL statement
set #sql = 'select ' + #sql
-- check the length : 33786
select LEN(#sql)
-- check the content
print #sql
-- execute to get the columns
exec (#sql)
Use an nvarchar(max) datatype for #sql.
Related
I have problem that replace function does not work
DECLARE #Tabela nvarchar(25)
DECLARE #query nvarchar(max)
SET #Tabela = '_#tmp_tt2_POS_racuni_'
SET #query = 'SELECT * INTO '+#Tabela+((replace(convert(varchar(10), getdate(),121),'''-''',''''))+'-'+(replace(convert(nvarchar(10),getdate(),108),''':''','''')))+'NP'+' FROM _tabels'
PRINT #query
SELECT *
INTO _#tmp_tt2_POS_racuni_2021-12-21-11:15:27NP
FROM _tabels
Completion time: 2021-12-21T11:15:27.0724917+01:00
You should use FORMAT and specify the format you want directly instead of going through intermediate formats. For example :
select format(getdate(),'yyyyMMddhhmmss')
Produces 20211221124017. FORMAT is slower than CONVERT but in this case it's only called once. It's far more important to write a readable query that produces the correct result.
That said, it's probably better to use table partitioning instead of creating lots of temporary tables with a date in the name. All supported SQL Server versions and editions support partitioning, even LocalDB
The quotes you use are two too many.
You are using replace(date,''':''',''''). This will replace ':' with ''. However, the getdate() doesn't have quotes itself. I guess you did that because of the dynamic sql you are using - but for the dates, you should omit the quotes:
replace(date,':','')
Firstly, let's get onto the real problem that is discussed at lengths in the comments; this is a terrible idea.
The fact you want to create a table for an exact point in time smells very strongly of an XY Problem. What is the real problem you are trying to solve with this? Most likely what you really want is a partitioned table or a temporal table, so that you can query the data for an exact point in time. Which you need, we don't know, but I would suggest that you rethink your "solution" here.
As for the problem, it's working exactly as intended. Let's look at your REPLACE in solitude:
replace(convert(varchar(10), getdate(),121),'''-''','''')
So, in the above, you want to replace '-' (a hyphen wrapped in single quotes) with '' (2 single quotes). You don't want to replace a hyphen (-) with a zero length string; that would be REPLACE(..., '-','').
The style you are using, 121 gives the format yyyy-mm-dd hh:mi:ss.mmm, which doesn't contain a single single quote ('), so no wonder it isn't finding the pattern.
Though you don't need REPLACE on that date at all. YOu are taking the first 10 characters or the style and then removing the hyphens (-) to get yyyyMMdd, but there is already a style for that; style 112.
The above could be rewritten as:
DECLARE #Tabela sysname;
DECLARE #query nvarchar(max);
SET #Tabela = N'_#tmp_tt2_POS_racuni_';
SET #query = N'SELECT * INTO dbo.'+QUOTENAME(CONCAT(#Tabela,CONVERT(nvarchar(8),GETDATE(),112),,N'-'.REPLACE(CONVERT(nvarchar(10),GETDATE(),108),':',''),N'',N'NP')+N' FROM dbo._tabels;'
PRINT #query;
On MSSQL when I run my query I get the following output when run as Results To Text:
Description Another Column
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------
This is a Description 43289
But interestingly there aren't any blanks at all at the end of the string, and the length is also the correct one (without any blanks)
I want to have an output like this:
Description Another Column
-------------- --------------
My description 543893
I've tried rtrim, ltrim, replace, but none is working. The datatype is nvarchar(max).
As I say, if you really need to do this, you have to do two passes through the data:
create table #t (description nvarchar(max),otherColumn int)
insert into #t(description,otherColumn) values
(N'Short Desc.',21),
(N'Loooooonnnnnngggggg Description',99)
declare #maxLength int
declare #sql nvarchar(max)
select #maxLength = MAX(LEN(description)) from #t
if #maxLength <= 4000
begin
set #sql = N'SELECT CONVERT(nvarchar(' + CONVERT(nvarchar(4),#maxLength) +
N'),description) as description'
end
else
begin
set #sql = N'SELECT description'
end
set #sql = #sql + N',otherColumn from #t'
exec sp_executesql #sql
Result:
description otherColumn
------------------------------- -----------
Short Desc. 21
Loooooonnnnnngggggg Description 99
Even if you use RTRIM, etc, when the system is compiling the query, it doesn't know the nature of the data contained in individual rows1. And so it can only assign the data type nvarchar(max) as the type of the resulting column.
In turn, when SSMS starts to receive the result set, it's informed that the column is an nvarchar(max). At the point at which it's printing the column headers, it doesn't know the nature of the lengths of the results it will receive, so it just has to adopt a sensible default display width that can cope with any data it receives.
I'd normally leave such format considerations to an actual presentation layer (e.g. report builder or application), rather than fiddling around and trying to force SQL/SMS to do this sort of formatting.
1E.g. one of the rows might have the description containing the complete works of Shakespeare, in the original Klingon.
Try this
select Description,len(rtrim(Description)) as count from table
Edit
The actual problem is you set the result mode to text. Change it to grid mode. You will get the correct result
I know this is old, but for anyone who might need it in the future (including myself with how bad my memory is).
I ran into this same problem. Here was the solution I came up with:
SELECT CASE
WHEN LEN([Description]) >= 20
THEN CAST((LEFT([Description], 17) + '...') AS VARCHAR(20))
ELSE CAST([Description] AS VARCHAR(20))
END AS [Description]
,[Another Column]
FROM [dbo].[YourTableName];
I hope you find this helpful, even though it is a bit of a long-winded way to do it.
Is it possible to save a multiline varchar in SQL Server?
DECLARE #SQL VARCHAR(256)
SELECT #SQL = 'SELECT
ID, Name
FROM
Cust'
INSERT INTO
MultiLineTBL (SQL)
VALUES
(#SQL)
So this query:
SELECT SQL From MultiLineTBL
will return:
SELECT
ID, Name
FROM
Cust
Not the straight line:
SELECT ID, NAME FROM Cust
How is it possible to store a multiline varchar?
Your sql query have syntex error:
DECLARE #SQL VARCHAR(256) ;
--please update query like that
set #SQL = 'insert into MultiLineTBL(col1,col2)
SELECT ID, Name FROM Cust';
--and execure like that:
exec(#SQL);
Yes, it is possible to store a multi-line varchar in a table. In fact, the value in your example is stored correctly.
As AlexK has correctly commented, multi-line values are not displayed correctly in SQL Server Management Studio when it is configured to return the results in a grid, and that (viewing the results in a grid) must be what gave you the wrong impression. As per my observation, SSMS replaces every control character, including CR (CHAR(13)) and LF (CHAR(10)), with a space in this display mode.
Try switching to Results to Text (Ctrl+T) before running the query and you will see that line delimiters are actually preserved.
I am passing a comma-delimited list of values into a stored procedure. I need to execute a query to see if the ID of an entity is in the comma-delimited list. Unfortunately, I think I do not understand something.
When I execute the following stored procedure:
exec dbo.myStoredProcedure #myFilter=N'1, 2, 3, 4'
I receive the following error:
"Conversion failed when converting the varchar value '1, 2, 3, 4' to data type int."
My stored procedure is fairly basic. It looks like this:
CREATE PROCEDURE [dbo].[myStoredProcedure]
#myFilter nvarchar(512) = NULL
AS
SET NOCOUNT ON
BEGIN
-- Remove the quote marks so the filter will work with the "IN" statement
SELECT #myFilter = REPLACE(#myFilter, '''', '')
-- Execute the query
SELECT
t.ID,
t.Name
FROM
MyTable t
WHERE
t.ID IN (#myFilter)
ORDER BY
t.Name
END
How do I use a parameter in a SQL statement as described above? Thank you!
You could make function that takes your parameter, slipts it and returns table with all the numbers in it.
If your are working with lists or arrays in SQL Server, I recommend that you read Erland Sommarskogs wonderful stuff:
Arrays and Lists in SQL Server 2005
You need to split the string and dump it into a temp table. Then you join against the temp table.
There are many examples of this, here is one at random.
http://blogs.microsoft.co.il/blogs/itai/archive/2009/02/01/t-sql-split-function.aspx
Absent a split function, something like this:
CREATE PROCEDURE [dbo].[myStoredProcedure]
#myFilter varchar(512) = NULL -- don't use NVARCHAR for a list of INTs
AS
SET NOCOUNT ON
BEGIN
SELECT
t.ID,
t.Name
FROM
MyTable t
WHERE
CHARINDEX(','+CONVERT(VARCHAR,t.ID)+',',#myFilter) > 0
ORDER BY
t.Name
END
Performance will be poor. A table scan every time. Better to use a split function. See: http://www.sommarskog.se/arrays-in-sql.html
I would create a function that takes your comma delimited string and splits it and returns a single column table variable with each value in its own row. Select that column from the returned table in your IN statement.
I found a cute way of doing this - but it smells a bit.
declare #delimitedlist varchar(8000)
set #delimitedlist = '|1|2|33|11|3134|'
select * from mytable where #delimitedlist like '%|' + cast(id as varchar) + '|%'
So... this will return all records with an id equal to 1, 2, 33, 11, or 3134.
EDIT:
I would also add that this is not vulnerable to SQL injection (whereas dynamic SQL relies on your whitelisting/blacklisting techniques to ensure it isn't vulnerable). It might have a performance hit on large sets of data, but it works and it's secure.
I have a couple of blog posts on this as well, with a lot of interesting followup comments and dialog:
More on splitting lists
Processing list of integers
I was actually asked this myself a few weeks ago, whereas I know exactly how to do this with a SP or UDF but I was wondering if there was a quick and easy way of doing this without these methods. I'm assuming that there is and I just can't find it.
A point I need to make is that although we know what characters are allowed (a-z, A-Z, 0-9) we don't want to specify what is not allowed (##!$ etc...). Also, we want to pull the rows which have the illegal characters so that it can be listed to the user to fix (as we have no control over the input process we can't do anything at that point).
I have looked through SO and Google previously, but was unable to find anything that did what I wanted. I have seen many examples which can tell you if it contains alphanumeric characters, or doesn't, but something that is able to pull out an apostrophe in a sentence I have not found in query form.
Please note also that values can be null or '' (empty) in this varchar column.
Won't this do it?
SELECT * FROM TABLE
WHERE COLUMN_NAME LIKE '%[^a-zA-Z0-9]%'
Setup
use tempdb
create table mytable ( mycol varchar(40) NULL)
insert into mytable VALUES ('abcd')
insert into mytable VALUES ('ABCD')
insert into mytable VALUES ('1234')
insert into mytable VALUES ('efg%^&hji')
insert into mytable VALUES (NULL)
insert into mytable VALUES ('')
insert into mytable VALUES ('apostrophe '' in a sentence')
SELECT * FROM mytable
WHERE mycol LIKE '%[^a-zA-Z0-9]%'
drop table mytable
Results
mycol
----------------------------------------
efg%^&hji
apostrophe ' in a sentence
Sql server has very limited Regex support. You can use PATINDEX with something like this
PATINDEX('%[a-zA-Z0-9]%',Col)
Have a look at PATINDEX (Transact-SQL)
and Pattern Matching in Search Conditions
I found this page with quite a neat solution. What makes it great is that you get an indication of what the character is and where it is. Then it gives a super simple way to fix it (which can be combined and built into a piece of driver code to scale up it's application).
DECLARE #tablename VARCHAR(1000) ='Schema.Table'
DECLARE #columnname VARCHAR(100)='ColumnName'
DECLARE #counter INT = 0
DECLARE #sql VARCHAR(MAX)
WHILE #counter <=255
BEGIN
SET #sql=
'SELECT TOP 10 '+#columnname+','+CAST(#counter AS VARCHAR(3))+' as CharacterSet, CHARINDEX(CHAR('+CAST(#counter AS VARCHAR(3))+'),'+#columnname+') as LocationOfChar
FROM '+#tablename+'
WHERE CHARINDEX(CHAR('+CAST(#counter AS VARCHAR(3))+'),'+#columnname+') <> 0'
PRINT (#sql)
EXEC (#sql)
SET #counter = #counter + 1
END
and then...
UPDATE Schema.Table
SET ColumnName= REPLACE(Columnname,CHAR(13),'')
Credit to Ayman El-Ghazali.
SELECT * FROM TABLE_NAME WHERE COL_NAME LIKE '%[^0-9a-zA-Z $#$.$-$''''$,]%'
This works best for me when I'm trying to find any special characters in a string