Alter ADD tempDB inside SELECT? - sql-server

How can I alter tempdb inside a select?
I want a single query is that possible?
SELECT cust_ac_no, ord_no, ref_no, net_svc_id, job_type, ord_status, ord_status_date, ord_crt_date
INTO tempdb..xtiankwiksetreport
ALTER table tempdb..xtiankwiksetreport
ADD serial_no varchar(25) null,
msisdn varchar(25) null,
imsi varchar(25) null,
bts_id varchar(25) null
FROM wo_order
WHERE job_type IN ('EXTR', 'EXTC')
AND svc_type='4G'
AND ref_no=2
AND ord_status IN ('PL', 'JL')

SELECT cust_ac_no, ord_no, ref_no, net_svc_id, job_type, ord_status, ord_status_date, ord_crt_date,
serial_no=convert(varchar(25), null), msisdn-convert(varchar(25), null), imsi=convert(varchar(25), null), bts_id=convert(varchar(25), null)
INTO tempdb..xtiankwiksetreport
FROM wo_order
WHERE job_type IN ('EXTR', 'EXTC')
AND svc_type='4G'
AND ref_no=2
AND ord_status IN ('JL', 'PL')

I have started to test this in Sybase before you added the sql-server tag so I will provide both, the first one explain the idea, the second will prove this is possible in both DBMS.
Note that the solution is working but I would not use this as a long term solution. I didn't research on the possibilities of this to be a documented behavior, and could fail in the future (or already, based on my database version)
Sybase
Just use a convert function on a null value to add the empty columns.
select 'a' as foo,
convert(varchar(25), null) as serial_no
into #temp
Then to test it, I insert a long value :
insert into #temp values ('b', 'abcdefghijklmnopqrstuvwxyz')
And the result :
select * from #temp
foo,serial_no
'a',
'b','abcdefghijklmnopqrstuvwxy'
The 26th character is missing, we have a varchar(25)
SQL-Server
The same code will provide an error due to the truncated value
select 'a' as foo, convert(varchar(25), null) as serial_no
into temp_foo
insert into temp_foo values ('b', 'abcdefghijklmnopqrstuvwxyz')
insert into temp_foo values ('c', 'abcdefghijklmnopqrstuvwxy')
select * from temp_foo
The line with b provide a value to long and gives me :
Msg 8152, Level 16, State 14, Line 4
String or binary data would be truncated.
The statement has been terminated.
But the line with c with 25 character fits.
foo,serial_no
'a',
'c','abcdefghijklmnopqrstuvwxy'
SELECT
cust_ac_no, ord_no, ref_no, net_svc_id, job_type, ord_status, ord_status_date, ord_crt_date,
convert(varchar(25), null) as serial_no,
convert(varchar(25), null) as msisdn,
convert(varchar(25), null) as imsi,
convert(varchar(25), null) as bts_id,
INTO tempdb..xtiankwiksetreport
FROM wo_order
WHERE job_type IN ('EXTR', 'EXTC')
AND svc_type='4G'
AND ref_no=2
AND ord_status IN ('PL', 'JL')

Related

'String or binary data would be truncated' without any data exceeding the length

Yesterday suddenly a report occurred that someone was not able to get some data anymore because the issue Msg 2628, Level 16, State 1, Line 57 String or binary data would be truncated in table 'tempdb.dbo.#BC6D141E', column 'string_2'. Truncated value: '!012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678'. appeared.
I was unable to create a repro without our tables. This is the closest as I can get to:
-- Create temporary table for results
DECLARE #results TABLE (
string_1 nvarchar(100) NOT NULL,
string_2 nvarchar(100) NOT NULL
);
CREATE TABLE #table (
T_ID BIGINT NULL,
T_STRING NVARCHAR(1000) NOT NULL
);
INSERT INTO #table VALUES
(NULL, '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'),
(NULL, '!0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789!');
WITH abc AS
(
SELECT
'' AS STRING_1,
t.T_STRING AS STRING_2
FROM
UT
INNER JOIN UTT ON UTT.UT_ID = UT.UT_ID
INNER JOIN MV ON MV.UTT_ID = UTT.UTT_ID
INNER JOIN OT ON OT.OT_ID = MV.OT_ID
INNER JOIN #table AS T ON T.T_ID = OT.T_ID -- this will never get hit because T_ID of #table is NULL
)
INSERT INTO #results
SELECT STRING_1, STRING_2 FROM abc
ORDER BY LEN(STRING_2) DESC
DROP TABLE #table;
As you can see the join of #table cannot yield any results because all T_ID are NULL nevertheless I am getting the error mentioned above. The result set is empty.
That would be okay if a text with more than 100 characters would be in the result set but that is not the case because it is empty. If I remove the INSERT INTO #results and display the results it does not contain any text with more than 100 characters. The ORDER BY was only used to determine the faulty text value (with the original data).
When I use SELECT STRING_1, LEFT(STRING_2, 100) FROM abc it does work but it does not contain the text either that is meant to be truncated.
Therefore: What am I missing? Is it a bug of SQL Server?
-- this will never get hit is a bad assumption. It is well known and documented that SQL Server may try to evaluate parts of your query before it's obvious that the result is impossible.
A much simpler repro (from this post and this db<>fiddle):
CREATE TABLE dbo.t1(id int NOT NULL, s varchar(5) NOT NULL);
CREATE TABLE dbo.t2(id int NOT NULL);
INSERT dbo.t1 (id, s) VALUES (1, 'l=3'), (2, 'len=5'), (3, 'l=3');
INSERT dbo.t2 (id) VALUES (1), (3), (4), (5);
GO
DECLARE #t table(dest varchar(3) NOT NULL);
INSERT #t(dest) SELECT t1.s
FROM dbo.t1
INNER JOIN dbo.t2 ON t1.id = t2.id;
Result:
Msg 2628, Level 16, State 1
String or binary data would be truncated in table 'tempdb.dbo.#AC65D70E', column 'dest'. Truncated value: 'len'.
While we should have only retrieved rows with values that fit in the destination column (id is 1 or 3, since those are the only two rows that match the join criteria), the error message indicates that the row where id is 2 was also returned, even though we know it couldn't possibly have been.
Here's the estimated plan:
This shows that SQL Server expected to convert all of the values in t1 before the filter eliminated the longer ones. And it's very difficult to predict or control when SQL Server will process your query in an order you don't expect - you can try with query hints that attempt to either force order or to stay away from hash joins but those can cause other, more severe problems later.
The best fix is to size the temp table to match the source (in other words, make it large enough to fit any value from the source). The blog post and db<>fiddle explain some other ways to work around the issue, but declaring columns to be wide enough is the simplest and least intrusive.

Can we stop SQL Server EXCEPT from ignoring trailing spaces in values

I am auditing values in 2 identical structure tables. The T-SQL EXCEPT statement is ignoring the trailing space on a value in one table, so the values don't match, but also do not show up in our audit.
I have tried searching for ways to change how SQL is comparing the columns. I did something similar to ensure it was case sensitive, but couldn't find something that would make it include the white space/padding in the field value.
Example data would have the value in MyTable as "Product Name ", while the RemoteTable has the value "Product Name".
To quickly reproduce, here is a slimmed down version of what I'm doing now:
DECLARE #SampleLocal TABLE(ProductName varchar(50))
DECLARE #RemoteTable TABLE(ProductName varchar(50))
INSERT INTO #SampleLocal (ProductName) VALUES ('Product Name')
INSERT INTO #RemoteTable (ProductName) VALUES ('Product Name ')
SELECT ProductName COLLATE SQL_Latin1_General_CP1_CS_AS ProductName
FROM #SampleLocal
EXCEPT
SELECT ProductName COLLATE SQL_Latin1_General_CP1_CS_AS ProductName
FROM #RemoteTable
This currently returns no results, showing that the values are the same. But the value in the second table has a space at the end.
I would expect to get a result back that has "Product Name"
When I needed to compare things with case sensitivity I was able to add
COLLATE SQL_Latin1_General_CP1_CS_AS
Is there something similar that would show the value being different because of the blank space?
According to this article (https://support.microsoft.com/en-us/help/316626/inf-how-sql-server-compares-strings-with-trailing-spaces) :
The ANSI standard requires padding for the character strings used in comparisons so that their lengths match before comparing them. The padding directly affects the semantics of WHERE and HAVING clause predicates and other Transact-SQL string comparisons. For example, Transact-SQL considers the strings 'abc' and 'abc ' to be equivalent for most comparison operations.
This behavior is intended.
You can use a slower method to achieve what you wanted:
SELECT innerItems.ProductName
FROM
(
SELECT DATALENGTH(ProductName) as realLength, ProductName COLLATE SQL_Latin1_General_CP1_CS_AS as ProductName
FROM #SampleLocal
EXCEPT
SELECT DATALENGTH(ProductName) as realLength, ProductName COLLATE SQL_Latin1_General_CP1_CS_AS as ProductName
FROM #RemoteTable
) innerItems
Comparing the values and real lengths together does the magic here. (The len method would give the 'wrong' result in this case)
This is old, and already answered, but I was struggling with this as a result of comparison in a JOIN condition too. While it is true that SQL Server ignores trailing spaces, it doesn't ignore leading spaces. Therefore for my JOIN condition, I compared forwards and backwards (using the reverse function) and that gave me a more accurate set of results.
In the example below, I want rows with a trailing space in one table to only match rows with a trailing space in the JOINed table. The REVERSE function helped with this.
DECLARE #DbData AS TABLE (
Id INT,
StartDate DATETIME,
OrgName VARCHAR(100)
)
DECLARE #IncomingData AS TABLE (
Id INT,
StartDate DATETIME,
OrgName VARCHAR(100)
)
INSERT INTO #DbData (Id, StartDate, OrgName)
SELECT 1, CAST('1 Jan 2022' AS DATE), 'Test ' UNION ALL
SELECT 2, CAST('1 Jan 2022' AS DATE), 'Test' UNION ALL
SELECT 3, CAST('1 Jan 2022' AS DATE), 'Other Test' UNION ALL
SELECT 4, CAST('1 Jan 2022' AS DATE), 'Other Test '
INSERT INTO #IncomingData (Id, StartDate, OrgName)
SELECT 1, CAST('1 Jan 2022' AS DATE), 'Test ' UNION ALL
SELECT 2, CAST('1 Jan 2022' AS DATE), 'Test' UNION ALL
SELECT 3, CAST('1 Jan 2022' AS DATE), 'Other Test'
SELECT '~' + dd.OrgName + '~', '~' + id.OrgName + '~', *
FROM #DbData dd
JOIN #IncomingData id ON id.StartDate = dd.StartDate
AND dd.OrgName = id.OrgName
AND REVERSE(dd.OrgName) = REVERSE(id.OrgName) -- Try the query with and without this line to see the difference it makes

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

Even though this tends to look as a duplicate, I had to post it as I can't seem to spot the error.
I don't know if I am mad or what but I can't seem to spot why there is a mismatch in the number of supplied values.
Here are they:
CREATE TABLE #TIPSTOPE_TS
(
TIP INT NULL,
SIFVAL VARCHAR(5),
GRUPA INT NULL,
DATUMOD VARCHAR(15),
PASIVNA DECIMAL(15,4) NULL DEFAULT(0),
REDOVNA DECIMAL(15,4) NULL DEFAULT(0),
ZATEZNA DECIMAL(15,4) NULL DEFAULT(0),
STOPA DECIMAL(15,4) NULL DEFAULT(0),
DATUMDO VARCHAR(15),
KONTO VARCHAR(15),
)
INSERT INTO #TIPSTOPE_TS
SELECT TS.TIP,
TS.SIFVAL,
TS.GRUPA,
CASE WHEN ISDATE(MAX(TS.DATUMOD)) = 0 THEN '2017.12.31' ELSE MAX(TS.DATUMOD) END AS DATUMOD,
CAST (2 AS DECIMAL(10,4)) AS PASIVNA,
CAST (1 AS DECIMAL(10,4)) AS REDOVNA,
CAST (3 AS DECIMAL(10,4)) AS ZATEZNA,
TS.REDOVNA,
TS.DATUMDO,
TP.M1 AS KONTO
FROM TIPSTOPE TS WITH(NOLOCK)
JOIN TIPPART TP WITH(NOLOCK) ON TP.TIP = TS.TIP
WHERE TS.DATUMOD <= '2017.12.31'
GROUP BY TS.TIP,TS.SIFVAL,TS.GRUPA,TP.M1,TS.DATUMDO,TS.REDOVNA
CREATE NONCLUSTERED INDEX IX_TIPSTOPE_TS ON #TIPSTOPE_TS (TIP, GRUPA, SIFVAL)
INCLUDE (DATUMOD)
And the second one...
CREATE TABLE #UNPVT_TIPSTOPE_TS
(
TIP INT NULL,
SIFVAL VARCHAR(5) NULL,
GRUPA INT NULL,
DATUMOD VARCHAR(10) NULL,
TIP_KS VARCHAR(15) NULL,
KAMATNA_STOPA DECIMAL(15,4) NULL DEFAULT(0),
DATUMDO VARCHAR(10) NULL,
)
INSERT INTO #UNPVT_TIPSOPE_TS
SELECT TIP, SIFVAL, GRUPA, DATUMOD, TIP_KS, KAMATNA_STOPA,DATUMDO
FROM
(
SELECT TIP, SIFVAL, GRUPA, DATUMOD, ISNULL(REDOVNA,0) AS REDOVNA, ISNULL(PASIVNA,0) AS PASIVNA, ISNULL(ZATEZNA,0) AS ZATEZNA,STOPA,DATUMDO
FROM #TIPSTOPE_TS
) P
UNPIVOT (KAMATNA_STOPA FOR TIP_KS IN (REDOVNA, PASIVNA, ZATEZNA)) AS UNPVT
The second temp tables is taking data from the first one.
When I try to create the second one error is thrown:
Insert error: Column name or number of supplied values does not match table definition
You are specifying the exact number of values that are needed. If you copy the whole code in new query window and execute it, it will work. Or in your current window drop the table table:
DROP TABLE #TIPSTOPE_TS;
DROP TABLE #UNPVT_TIPSTOPE_TS;
I mean execute only the above statements, and the execute the rest of the code. It should work again.
Sometime, when are debugging we forgot that the temporary table meta data is cached. For example, you can have the following code:
DROP TABLE IF EXISTS #TEST;
CREATE TABLE #TEST
(
[A] INT
);
INSERT INTO #TEST ([A])
SELECT 1;
And its valid. If we change it to this:
DROP TABLE IF EXISTS #TEST;
CREATE TABLE #TEST
(
[A] INT
,[B] INT
);
INSERT INTO #TEST ([A], [B])
SELECT 1, 2;
We will get:
Msg 207, Level 16, State 1, Line 9 Invalid column name 'B'.
Because, in the current session the #TEST table already exists and the engine is able to check that the B column does not exists. So, we need to drop the table manually, after the columns are changed, or we need to drop the tables at the end of our code statements.

Stored Procedure SELECT UPDATE Incorrect Values

I am creating a stored procedure, which I intend on running via a job every 24 hours. I am able to successfully run the procedure query but for some reason the values dont seem to make sense. See below.
This is my table and what it looks like prior to the running of the procedure, using the following statement:
SELECT HardwareAssetDailyAccumulatedDepreciationValue,
HardwareAssetAccumulatedDepreciationValue FROM HardwareAsset
I then run the following procedure (with the intention of basically copying the value in DailyDepreciationValue to DepreciationValue):
BEGIN
SELECT HardwareAssetID, HardwareAssetDailyAccumulatedDepreciationValue,
HardwareAssetAccumulatedDepreciationValue FROM HardwareAsset
WHERE HardwareAssetDailyAccumulatedDepreciationValue IS NOT NULL
UPDATE HardwareAsset SET HardwareAssetAccumulatedDepreciationValue = CASE WHEN
(HardwareAssetAccumulatedDepreciationValue IS NULL) THEN
CONVERT(DECIMAL(7,2),HardwareAssetDailyAccumulatedDepreciationValue) ELSE
CONVERT(DECIMAL(7,2),(HardwareAssetAccumulatedDepreciationValue + HardwareAssetDailyAccumulatedDepreciationValue))
END
END
But when i re-run the select statement the results are as follows:
It really doesnt make any sense to me at all any ideas?
I am not able to replicate. We need more detail on the table structure and data. This is what I used to attempt to replicate. Feel free to modify as needed:
create table #t (
AccD1 decimal(7,2)
, AccD2 decimal(7,2)
, AccDaily as AccD1 + AccD2
, AccTotal decimal(7,2)
)
insert #t values
(100, 7.87, null)
, (300, 36.99, null)
, (400, 49.32, null)
, (100, 50.00, 100)
select * from #t
update #t set
AccTotal = isnull(AccTotal, 0) + AccDaily
, AccD1 = 0
, AccD2 = 0
select * from #t
drop table #t

Using ISNUMERIC fails in condition

I have a table like this (simplified):
CREATE TABLE #table (Id INT, Field NVARCHAR(MAX))
INSERT INTO #table VALUES (1, 'SomeText')
INSERT INTO #table VALUES (2, '1234')
For some reasons I need to query this table and get the sum of Field if it is numeric and return '' if it is not. I tried it like this:
SELECT CASE WHEN ISNUMERIC(Field) = 1 THEN SUM(CONVERT(MONEY, Field)) ELSE '' END
FROM #table
GROUP BY Field
But this query leads to the following exception:
Cannot convert a char value to money. The char value has incorrect syntax.
I even changed the ELSE case from '' to 0 but I still get the same message.
Why do I get the exception? As far as I know, SUM(...) should not be executed when ISNUMERIC(Field) returns 0.
Select sum(case when ISNUMERIC(Field)=1 then cast(field as money) else 0 end)
from #table
Group By Field
Returns
(No column name)
1234.00
0.00
Working with mixed datatypes can be a real pain. Where possible, consider table designs that avoid this. To further complicate matters, IsNumeric does not always return what you might expect.
Filtering out the non-numerics before aggregating is one way to go:
SELECT
SUM(CONVERT(MONEY, Field))
FROM
#table
WHERE
ISNUMERIC(Field) = 1
GROUP BY
Field
;

Resources