How to UPSERT a record in SAP ASE Sybase 16? - sybase

I am literaly following the SAP documentation 1st example on UPSERT a record in ASE:
https://help.sap.com/viewer/cbed2190ee2d4486b0bbe0e75bf4b636/16.0.3.2/en-US/faf583d9adc547ad8a164bb3f41ea6cd.html
1> select ##version
2> go
Adaptive Server Enterprise/16.0 SP03 PL06/EBF 28334 SMP/P/x86_64/SLES 11.1/ase1
60sp03pl06x/3457/64-bit/FBO/Mon Nov 26 04:33:30 2018
(1 row affected)
1> select * from t1
2> go
a b c
----------- ----------- -----------
1 2 3
(1 row affected)
1> upsert t1(a,b,c) values(1,2,3)
2> go
Msg 102, Level 15, State 181:
Server 'NPL', Line 1:
Incorrect syntax near 'a'.
Does anyone know why am I getting this "Incorrect syntax" error in the UPSERT statment?
Thanks

If the sole intent is to implement upsert capability in ASE then what you want to look at is the merge command.
If the intent is to utilize (a subset of) HANA's SQLScript (in this case the upsert command) for some sort of interoperability requirement, and keeping in mind you may need to modify existing code to work with dual (and incompatible) parsers, then ...
To use (a limited version of) HANA's SQLScript in ASE you first need to create a database that supports the SQLScript parser (see Creating a SQLScript database), eg:
use master
go
create database sqlscript_db
on data_01=10
log on log_01=5
for sqlscript -- enable use of SQLScript parser
go
Running sp_helpdb (from a non-SQLScript db) to verify db status:
use master
go
sp_helpdb sqlscript_db
go
name db_size owner dbid created durability lobcomplvl inrowlen status
------------ ------------- ----- ---- ------------ ---------- ---------- -------- ---------
sqlscript_db 15.0 MB sa 7 Mar 25, 2022 full 0 NULL sqlscript
... snip ... ^^^^^^^^^
You should now be able to use the upsert statement in this new database:
use sqlscript_db
go
create table t1 (a int, b int, c int)
go
upsert t1(a,b,c) values(1,2,3)
go
(1 row affected)
select * from t1
go
a b c
----------- ----------- -----------
1 2 3
NOTE: verified on ASE 16.0 SP04 GA
Verifying SQLScript/upsert does not work in a non-SQLScript database:
use tempdb
go
create table t1 (a int, b int, c int)
go
upsert t1(a,b,c) values(1,1,1)
go
Msg 102, Level 15, State 181:
Server 'ASE400', Line 1:
Incorrect syntax near 'a'.

if your unique key, let's say, is t1.a, then you can use the logic below:
merge into t1 as dest
using (select 1 a, 2 b, 3 c) as src
on dest.a = src.a
when not matched then
insert (a,b,c) values(src.a,src.b,src.c)
when matched then
update set b=src.b, c=src.c

Related

DB2 trigger to Insert/Update records into different database

I want to create a trigger on one database's table and want to add that `records into another database's table.
Let us suppose, I have one table on first database, which has 5 rows and 2 columns. Another side I have one table on another
database, which has 3 rows and 2 columns, where 3 rows of another database's table are exact same as 3 rows of the first database's table.
I know, how to trigger the Insert/Update table on the same database. But how to trigger table from one database to another database?
Below is the code for triggering the tables in same database.
database_1 ---> schema_1 ---> table_1
|col1 col2|
_____|_____
|1a 1b |
|2a 2b |
|3a 3b |
|4a 4b |
|5a 5b |
database_2 ---> schema_2 ---> table_2
|col1 col2|
_____|_____
|1a 1b |
|2a 2b |
|3a 3b |
CREATE OR REPLACE TRIGGER "SCHEMA_1"."TRG_table_1_AFTER_UPDATE"
AFTER UPDATE ON "SCHEMA_1"."table_1"
REFERENCING NEW AS new_row
FOR EACH ROW
NOT SECURED
Insert into SCHEMA_2.TABLE_2(col1, col2, col3)
VALUES (new_row.val1, new_row.val2, new_row.val3);
END
No way to do it with triggers.
The way to update tables in another database is use of nicknames.
But CREATE TRIGGER statement states:
SQL-procedure-statement
Specifies the SQL statement that is to be part of the triggered action. A searched update, searched delete, insert, or merge operation
on nicknames inside compound SQL is not supported.
and
A procedure that contains a reference to a nickname in a searched
UPDATE statement, a searched DELETE statement, or an INSERT statement
is not supported (SQLSTATE 25000).
You may use some procedural logic with, say, 2PC-enabled federated servers, but not triggers.
Enabling two-phase commit for federated transactions
Update:
You should familiarize yourself with the concept of Federation in Db2 firstly.
The key technical topics for Db2 -> Db2 federation are:
Enabling the federated server to access data sources (update dbm cfg parameter if needed and restart the federated server instance).
Configuring remote Db2 data source information:
On federation server:
CREATE WRAPPER DRDA;
-- MYREMDB the alias of a cataloged remote database
CREATE SERVER MYSERVER
TYPE DB2/UDB
VERSION '11.5'
WRAPPER "DRDA"
AUTHORIZATION some_user PASSWORD "some_password"
OPTIONS
(
DBNAME 'MYREMDB'
, DB2_TWO_PHASE_COMMIT 'Y'
-- may be other options like:
, DB2_MAXIMAL_PUSHDOWN 'Y'
);
-- User mapping for some MY_LOCAL_USER
-- all work from MY_LOCAL_USER with remote tables will be with
-- this MY_REMOTE_USER account.
-- The corresponding GRANT statements must be run on
-- MY_LOCAL_USER locally and MY_REMOTE_USER remotely
-- to work with the corresponding tables
CREATE USER MAPPING FOR MY_LOCAL_USER
SERVER MYSERVER
OPTIONS
(
REMOTE_AUTHID 'my_remote_user'
, REMOTE_PASSWORD 'my_remote_password'
);
-- Create a nickname or use 3-part name directly in your statements
-- MYSERVER.MY_REMOTE_SCHEMA.MY_REMOTE_TABLE
CREATE NICKNAME MY_SCHEMA.MY_REMOTE_TABLE_NICKNAME
FOR MYSERVER.MY_REMOTE_SCHEMA.MY_REMOTE_TABLE;
-- Usage
-- Switch the autocommit off in your session
-- Both statements are either committed or rolled back successfully in their databases
-- because of 2PC option (DB2_TWO_PHASE_COMMIT) of the server MYSERVER
-- disregarding of what or where fails
INSERT INTO MY_LOCAL_TABLE ...;
INSERT INTO MY_SCHEMA.MY_REMOTE_TABLE_NICKNAME ...;
-- OR
-- INSERT INTO MYSERVER.MY_REMOTE_SCHEMA.MY_REMOTE_TABLE ...;
COMMIT;

How to get row values when we run SQL query from Linux Shell script

My shellscript contains below code to retrieve rows from a table in SQL server.
OUTPUT=aa.out
DATE_STR=20220103
{$ISQL} -U{$SQL_UU} -P{$SQL_PP} -S{$SERVER_NAME} << !! > {$OUTPUT}
GO
USE {$DB_NAME}
SELECT EMP_NAME FROM EMPLOYEE WHERE CONVERT(CHAR(8), LOGIN_TIME,112)={$DATE_STR}
GO
!!
I'm getting output in aa.out as below
locale is en_US.UFT-8
locale charset is UFT-8
using default charset UFT-8
1>2>3>4>EMP_NAME
ALICE
BOB
(1 row affected)
1>
how to modify my query or shellscript to get output
ALICE
BOB

How to fix a stored procedure that can recover deleted data

I have lately stumbled upon a blog post that talks about a stored procedure called Recover_Deleted_Data_Proc.sql that can apparently recover your deleted data from the .log file.
There is nothing new under the sun, we are going to use fn_dblog.
STEPS TO REPRODUCE
We are first going to create the table:
--Create Table
CREATE TABLE [Test_Table]
(
[Col_image] image,
[Col_text] text,
[Col_uniqueidentifier] uniqueidentifier,
[Col_tinyint] tinyint,
[Col_smallint] smallint,
[Col_int] int,
[Col_smalldatetime] smalldatetime,
[Col_real] real,
[Col_money] money,
[Col_datetime] datetime,
[Col_float] float,
[Col_Int_sql_variant] sql_variant,
[Col_numeric_sql_variant] sql_variant,
[Col_varchar_sql_variant] sql_variant,
[Col_uniqueidentifier_sql_variant] sql_variant,
[Col_Date_sql_variant] sql_variant,
[Col_varbinary_sql_variant] sql_variant,
[Col_ntext] ntext,
[Col_bit] bit,
[Col_decimal] decimal(18,4),
[Col_numeric] numeric(18,4),
[Col_smallmoney] smallmoney,
[Col_bigint] bigint,
[Col_varbinary] varbinary(Max),
[Col_varchar] varchar(Max),
[Col_binary] binary(8),
[Col_char] char,
[Col_timestamp] timestamp,
[Col_nvarchar] nvarchar(Max),
[Col_nchar] nchar,
[Col_xml] xml,
[Col_sysname] sysname
)
And we then insert data into it:
--Insert data into it
INSERT INTO [Test_Table]
([Col_image]
,[Col_text]
,[Col_uniqueidentifier]
,[Col_tinyint]
,[Col_smallint]
,[Col_int]
,[Col_smalldatetime]
,[Col_real]
,[Col_money]
,[Col_datetime]
,[Col_float]
,[Col_Int_sql_variant]
,[Col_numeric_sql_variant]
,[Col_varchar_sql_variant]
,[Col_uniqueidentifier_sql_variant]
,[Col_Date_sql_variant]
,[Col_varbinary_sql_variant]
,[Col_ntext]
,[Col_bit]
,[Col_decimal]
,[Col_numeric]
,[Col_smallmoney]
,[Col_bigint]
,[Col_varbinary]
,[Col_varchar]
,[Col_binary]
,[Col_char]
,[Col_nvarchar]
,[Col_nchar]
,[Col_xml]
,[Col_sysname])
VALUES
(CONVERT(IMAGE,REPLICATE('A',4000))
,REPLICATE('B',8000)
,NEWID()
,10
,20
,3000
,GETDATE()
,4000
,5000
,getdate()+15
,66666.6666
,777777
,88888.8888
,REPLICATE('C',8000)
,newid()
,getdate()+30
,CONVERT(VARBINARY(8000),REPLICATE('D',8000))
,REPLICATE('E',4000)
,1
,99999.9999
,10101.1111
,1100
,123456
,CONVERT(VARBINARY(MAX),REPLICATE('F',8000))
,REPLICATE('G',8000)
,0x4646464
,'H'
,REPLICATE('I',4000)
,'J'
,CONVERT(XML,REPLICATE('K',4000))
,REPLICATE('L',100)
)
GO
We are now going to verify if the data are there:
--Verify the data
SELECT * FROM Test_Table
At this point we need to create the stored procedure. I couldn't paste it here because it's too long but you can download it from the same blog post there is a link to a Box file.
If the query gives you troubles like this:
Msg 50000, Level 16, State 1, Procedure Recover_Deleted_Data_Proc, Line 22 [Batch Start Line 700] The compatibility level should be equal to or greater SQL SERVER 2005 (90)
Msg 50000, Level 16, State 1, Procedure Recover_Deleted_Data_Proc, Line 22 [Batch Start Line 705] The compatibility level should be equal to or greater SQL SERVER 2005 (90)
Is because you have to comment out from line 701 to line 708.
Cool, let's now delete the data from that table:
--Delete the data
DELETE FROM Test_Table
And confirm that the data were deleted:
--Verify the data
SELECT * FROM Test_Table
And here is the last step: we need to try to recover the data using the freshly installed stored procedure.
The author instruct us to use one of these two commands (don't forget to change 'test' with the name of your database):
--Recover the deleted data without date range
EXEC Recover_Deleted_Data_Proc 'test', 'dbo.Test_Table'
or
--Recover the deleted data it with date range
EXEC Recover_Deleted_Data_Proc 'test', 'dbo.Test_Table', '2012-06-01', '2012-06-30'
But the problem is that both returns this error:
(8 rows affected)
(2 rows affected)
(64 rows affected)
(2 rows affected)
(1 row affected)
(1 row affected)
(1 row affected)
(1 row affected)
(1 row affected)
(1 row affected)
Msg 245, Level 16, State 1, Procedure Recover_Deleted_Data_Proc, Line 485 [Batch Start Line 112]
Conversion failed when converting the varchar value '0x41-->01 ; 0001' to data type int.
If I right click on the stored procedure and I click "Modify", I don't see anything particularly fishy at Line 485.
Any idea why this stored procedure is not working?
What is the conversion mentioned?
The code is 10 years old and was written with the assumption that a [PAGE ID] would only ever be expressed as a pair of integers, e.g. 0001:00000138 - however, as you have learned, sometimes that is expressed differently, like 0x41-->01 ; 0001:00000138.
You can fix that problem by adding this inside the cursor:
IF #ConsolidatedPageID LIKE '0x%-->%;%'
BEGIN
SET #ConsolidatedPageID = LTRIM(SUBSTRING(#ConsolidatedPageID,
CHARINDEX(';', #ConsolidatedPageID) + 1, 8000));
END
But then your next problem is when you saved the procedure from the box file it probably changed '†' to some wacky ? character. When I fixed that (using N'†' of course, since Unicode characters should always have N), I still got these error messages:
Msg 537, Level 16, State 3, Procedure Recover_Deleted_Data_Proc, Line 525
Invalid length parameter passed to the LEFT or SUBSTRING function.
Msg 9420, Level 16, State 1, Procedure Recover_Deleted_Data_Proc, Line 651
XML parsing: line 1, character 2, illegal xml character
After 15 minutes of trying to reverse engineer this spaghetti, I gave up. If you need to recover data you deleted, restore a backup. If you don't have a backup, well, that's why we take backups. The fragile scripts people try to create to compensate for not taking backups are exactly why log recovery vendors charge the big bucks.
As an aside, the compatibility level error message is a red herring, totally misleading as the logic is currently written, and completely irrelevant to the problem. But it can be solved if, right before this:
IF ISNULL(#Compatibility_Level,0)<=80
BEGIN
RAISERROR('The compatibility level should ... blah blah',16,1)
RETURN
END
You add this:
IF DB_ID(#Database_Name) IS NULL
BEGIN
RAISERROR(N'Database %s does not exist.',11,1,#Database_name);
RETURN;
END
Or simply not calling those two example calls at the end of the script, since they depend on you having a database called test, which clearly you do not.

Comparing text to varchar(max) parameter gives String or binary data would be truncated

I can insert the longer than 8k text into a TEXT column, but for some reason I'm not able to do a LIKE with the exact same data. Can someone please explain what I'm doing wrong?
BEGIN TRAN
CREATE TABLE #log (
body [text] NULL
)
GO
DECLARE #longString varchar(max)
SET #longString = 'Ask Question
I am involved in a data migration project. I am getting the following error when I try to insert data from one table into another table (SQL Server 2005):
Msg 8152, Level 16, State 13, Line 1
String or binary data would be truncated.
The source data columns match the data type and are within the length definitions of the destination table columns so I am at a loss as to what could be causing this error.
sql-server tsql sql-server-2005 migration data-migration
shareeditflag
edited Sep 24 ''18 at 15:31
Lukasz Szozda
79.5k1061105
asked Jun 17 ''11 at 16:24
Jim Evans
3,44092954
Would you mind posting some code, and information about each table? – Kevin Mansel Jun 17 ''11 at 16:27
The tables are both quite large - so I will post only the part of the table definintions that are involved and the code - is that acceptable? – Jim Evans Jun 17 ''11 at 16:30
The table definitions and the code would be great. – IAmTimCorey Jun 17 ''11 at 16:31
add a comment
start a bounty
19 Answers
active oldest votes
141
You will need to post the table definitions for the source and destination tables for us to figure out where the issue is but the bottom line is that one of your columns in the source table is bigger than your destination columns. It could be that you are changing formats in a way you were not aware of. The database model you are moving from is important in figuring that out as well.
shareeditflag
edited Sep 4 ''14 at 18:11
Ryan Kohn
7,782104478
answered Jun 17 ''11 at 16:30
IAmTimCorey
13.8k52756
1
Per my comment above - comming shortly :) – Jim Evans Jun 17 ''11 at 16:32
3
I had faced the same problem and had to compare all the column types and sizes of both the tables to fix the issue. – Aziz Shaikh Jun 17 ''11 at 16:40
1
After going thourgh the exeecise of gathering the partial table definitions and then getting my sproc code the offending column jumped out at me like a lightning bolt... Thanks all for your input. – Jim Evans Jun 17 ''11 at 16:47
I can''t tell you how many times I''ve done the same thing. Glad you were able to solve your issue. – IAmTimCorey Jun 17 ''11 at 16:54
I marked you first reply as the answer because it was what led me to find the answer:) – Jim Evans Jun 17 ''11 at 17:25
add a comment | show 1 more comment
0
SQL Server 2019 will finally return more meaningful error message.
Binary or string data would be truncated => error message enhancments
if you have that error (in production), it''s not obvious to see which column or row this error comes from, and how to locate it exactly.
To enable new behavior you need to use DBCC TRACEON(460). New error text from sys.messages:
SELECT * FROM sys.messages WHERE message_id = 2628
2628 – String or binary data would be truncated in table ‘%.*ls’, column ‘%.*ls’. Truncated value: ‘%.*ls’.
String or Binary data would be truncated: replacing the infamous error 8152
This new message is also backported to SQL Server 2017 CU12 (and in an upcoming SQL Server 2016 SP2 CU), but not by default. You need to enable trace flag 460 to replace message ID 8152 with 2628, either at the session or server level.
Note that for now, even in SQL Server 2019 CTP 2.0 the same trace flag 460 needs to be enabled. In a future SQL Server 2019 release, message 2628 will replace message 8152 by default.
SQL Server 2017 CU12 also supports this feature.
Improvement: Optional replacement for "String or binary data would be truncated" message with extended information in SQL Server 2017
This SQL Server 2017 update introduces an optional message that contains the following additional context information.
Msg 2628, Level 16, State 6, Procedure ProcedureName, Line Linenumber
String or binary data would be truncated in table ''%.*ls'', column ''%.*ls''.
Truncated value: ''%.*ls''.
The new message ID is 2628. This message replaces message 8152 in any error output if trace flag 460 is enabled.
db<>fiddle demo
shareeditflag
edited Dec 4 ''18 at 20:20
answered Sep 24 ''18 at 15:29
Lukasz Szozda
79.5k1061105
add a comment
0
I wrote a useful store procedure to help identify and resolve the problem of text truncation (String or binary data would be truncated) when the INSERT SELECT statement is used. It compares fields CHAR, VARCHAR, NCHAR AND NVARCHAR only and returns an evaluation field by field in case of being the possible cause of the error.
EXEC dbo.GetFieldStringTruncate SourceTableName, TargetTableName
This stored procedure is oriented to the problem of text truncation when an INSERT SELECT statement is made.
The operation of this stored procedure depends on the user previously identifying the INSERT statement with the problem. Then inserting the source data into a global temporary table. The SELECT INTO statement is recommended.
You must use the same name of the field of the destination table in the alias of each field of the SELECT statement.
FUNCTION CODE:
DECLARE #strSQL nvarchar(1000)
IF NOT EXISTS (SELECT * FROM dbo.sysobjects where id = OBJECT_ID(N''[dbo].[GetFieldStringTruncate]''))
BEGIN
SET #strSQL = ''CREATE PROCEDURE [dbo].[GetFieldStringTruncate] AS RETURN''
EXEC sys.sp_executesql #strSQL
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/*
------------------------------------------------------------------------------------------------------------------------
Description:
Syntax
---------------
dbo.GetFieldStringTruncate(SourceTable, TargetTable)
+---------------------------+-----------------------+
| SourceTableName | VARCHAR(255) |
+---------------------------+-----------------------+
| TargetTableName | VARCHAR(255) |
+---------------------------+-----------------------+
Arguments
---------------
SourceTableName
The name of the source table. It should be a temporary table using double charp ''##''. E.g. ''##temp''
TargetTableName
The name of the target table. It is the table that receives the data used in the INSERT INTO stament.
Return Type
----------------
Returns a table with a list of all the fields with the type defined as text and performs an evaluation indicating which field would present the problem of string truncation.
Remarks
----------------
This stored procedure is oriented to the problem of text truncation when an INSERT SELECT statement is made.
The operation of this stored procedure depends on the user previously identifying the INSERT statement with the problem. Then inserting the source data into a global temporary table. The SELECT INTO statement is recommended.
You must use the same name of the field of the destination table in the alias of each field of the SELECT statement.
Examples
====================================================================================================
--A. Test basic
IF EXISTS (SELECT * FROM sys.objects WHERE OBJECT_ID = OBJECT_ID(N''[dbo].[tblDestino]'') AND TYPE IN (N''U''))
DROP TABLE tblDestino
CREATE TABLE tblDestino
(
Id INT IDENTITY,
Field1 VARCHAR(10),
Field2 VARCHAR(12),
Field3 VARCHAR(11),
Field4 VARCHAR(16),
Field5 VARCHAR(5),
Field6 VARCHAR(1),
Field7 VARCHAR(1),
Field8 VARCHAR(6),
Field9 VARCHAR(6),
Field10 VARCHAR(50),
Field11 VARCHAR(50),
Field12 VARCHAR(50)
)
INSERT INTO dbo.tblDestino
(
Field1 ,
Field2 ,
Field3 ,
Field4 ,
Field5 ,
'
PRINT LEN(#longString )
INSERT INTO #log ( body) VALUES (#longString )
SELECT * FROM #log WHERE body LIKE #longString
DROP TABLE #log
ROLLBACK TRAN
As per documentation:
pattern Is the specific string of characters to search for in
match_expression, and can include the following valid wildcard
characters. pattern can be a maximum of 8,000 bytes.
source https://learn.microsoft.com/en-us/sql/t-sql/language-elements/like-transact-sql?view=sql-server-2017
so you cannot use string that is longer in 8000 characters after LIKE operator, if you do it will be truncated hence you get an error.

"Select * from ..." doesn´t work, "select code, descr from..." does, why?

I run this query on SQL Server and it doesn't work:
SELECT * FROM dbo.marcas
but if I put at least one field in the query, it works.
SELECT code FROM dbo.marcas
I know it must be simple, but I can't find an answer.
Thansk
Most likely, someone else is updating that same table, and thus places certain locks on the table.
When you do a SELECT * ... those locks will cause a conflict and your query won't execute, while a SELECT (list of columns)...... will work (since it's not affected by the locks)
I'm answering my own question because I have found the answer by myself.
Using EMS Sql Manager 2008 for SQL Server I executed select * from marcas and have no results, just errors. But If I recreated the table, voila, it just worked fine !!!
So the problem was the way I created the tables in the server. After a while, I realized the command that created the table in Foxpro using ODBC was:
oerr = sqlexec(oconn, "ALTER TABLE ["+xtabla+"] ADD ["+borrar.field_name+"] "+tipo_campo(borrar.field_type, borrar.field_len, borrar.field_dec),"")
so changed it to:
oerr = sqlexec(oconn, "ALTER TABLE ["+xtabla+"] ADD ["+alltrim(borrar.field_name)+"] "+tipo_campo(borrar.field_type, borrar.field_len, borrar.field_dec),"")
that is, I just deleted the extra spaces right after the table name.
Thats all, "codigo" is not equal to "codigo ".
Thanks to all of you who tried to help me.
I beleve
One possibility would be if you have a computed column in the table that's generating an error when SQL Server attempts to compute it. Sample code:
create function dbo.Crash ()
returns int
as
begin
return 1/0
end
go
create table dbo.cctest (
Col1 int not null,
Col2 int not null,
CrashCol as dbo.Crash()
)
go
insert into dbo.cctest (Col1,Col2)
select 1,2 union all
select 3,4
go
select Col1 from dbo.cctest
go
select * from dbo.cctest
go
results:
Col1
----
1
3
(2 row(s) affected)
Col1 Col2 CrashCol
--------------------
(2 row(s) affected)
Msg 8134, Level 16, State 1, Line 1
Divide by zero error encountered.
So the first select worked since it didn't access the fault computed column
I recommend running the query in a sql client other than EMS, in the hope that you can get an informative error message.
"La operación en varios pasos generó errores. Compruebe los valores de estado." -> "The multi-step operation generated errors. Check the status values."

Resources