How do I select from a stored procedure in Sybase? - tsql

My DBA has constructed me a stored procedure in a Sybase database, for which I don't have the definition.
If I run it, it returns a resultset with a set of columns and values. I would like to SELECT further to reduce the rows in the result set. Is this possible?
From this question it seems like I could insert the results into a temporary table, but I'm not sure I've got permissions to do this.
Is there any way I can SELECT certain rows, or if not, can someone give me example code for simulating with a temporary table?

In Sybase ASE, we can use this hack to select from a stored procedure via a "proxy table":
http://www.sypron.nl/proctab.html
Example:
sp_addserver loopback, null, ##servername
go
create existing table
sp_test12 (
Document_Name varchar(100),
Required_Status varchar(5),
Doc_ID varchar(10),
OrderBy int,
No_of_Copy_Retain int,
_p_EPEB_ID varchar(10) null,
_p_MY_NAME varchar(3) null,
_p_MY_NO varchar(10) null,
_p_EPEB_EDATE datetime null,
_TXN varchar(10) null,
_SUBTXN varchar(15) null,
_OwnType_ID1 varchar(5) null,
_OwnType_ID2 varchar(5) null,
_blnflag int null
)
external procedure
at 'loopback.MYDB.dbo.usp_xyz'
go
select
Doc_ID, No_of_Copy_Retain, _p_EPEB_ID, _p_EPEB_ID, _p_MY_NAME, _p_MY_NO
from #sp_test12
where
_p_EPEB_ID='EPEB1508'
and _p_MY_NAME='107'
and _p_MY_NO='2011000045'
and _p_EPEB_EDATE='2011-01-15 15:03:03.0'
and _TXN='TX012'
and _SUBTXN='TX012.001'
and _OwnType_ID1='ASSN'
and _OwnType_ID2='ASSN'
and _blnflag=0
go

Under Sybase IQ (12.6 and higher at least) you can select from a stored procedure and filter the results as if it was a table. I do not know if this works under ASE or ASA but you could give it a try.
So if you stored procedure is called myproc an the result set has a column ACTIVE which can be either 0 or 1 and you want to select only the ACTIVE = 1 rows you could do this.
SELECT * FROM myproc() WHERE ACTIVE = 1
Under IQ you can also use this as a derived table and JOIN it with other tables for example like this...
SELECT t1.name,t1.address,t2,active
FROM tbl_atable t1,
( SELECT * FROM myproc() WHERE ACTIVE = 1) t2
WHERE t1.active = t2.active
...which is kind of neat!
I hope that works for which ever version of Sybase you are running.

You will need to ask the DBA to change the stored procedure.
You could get it changed to select the results into a temporary table rater than a plain select and then you can write your own select on that temp table to return only the rows you want

It is possible with ASE but in a rather roundabout kind of way using CIS and proxy tables. The mechanism is described very well on Rob Verschoor's site:
http://www.sypron.nl/proctab.html
I tried it out once as a curiosity and indeed it does work. I did not delve into the tricky question of error-handling.
pjjH

As far as I know, this is not possible in Sybase ASE. Even using
insert #temp_table
exec my_procedure
doesn't work (at least on sybase 12.x).

Just a thought.
Perhaps your DBA could prepare a view instead of a stored procedure, if he wanted you for some reason not to look at the inner stuff or worry about it.
Another approach would be to see the stored procedure text (unless encrypted) with sp_helptext and rewrite it for your own purposes (eg. into a view) to be able to apply additional conditioning to the resultset.

In Sybase IQ, you can do this:
select < col1>, < col2> from < sp_name>('< sp_arg>') where < predicate>
Example:
select Object, DbspaceName, ObjSize from sp_iqindexinfo ('table xyz') where Object like '%col1_indx%'

Related

Generate a unique uuid for each row in a table with Postgres

I have a UUID constraint set up as my Id field in one of my tables, however despite this (I think due to the fact that uuid_generate_v4 only creates on UUID per transaction?) when I imported a load of CSV data into my table, each row in the table was given the same UUID.
I want to be able to change this and give each row a unique UUID, however running
update monitors_nontest set id = uuid_generate_v1()
Again only produces one UUID for each row.
How can I change this command so that each row gets a different UUID?
Just a possibility. How did you actually generate the uuid? The function uuid_generate_v1() actually generates a different value each time it is called. But that is the key each time it is called. It seems if called within a sub select the Postgres optimizer may feel it can bypass the call and use cached result. Try the following.
create table uuid_gen( id1a uuid
, id1b uuid
, num1 integer
);
insert into uuid_gen(num1)
select generate_series(1,50);
update uuid_gen set id1a = uuid_generate_v1();
update uuid_gen set id1b = (select uuid_generate_v1());
select count(distinct id1a), count(distinct id1b) from uuid_gen;
Unfortunately, I could not find a fiddle processor that had the function uuid_generate_v1() available, nor uuid_generate_v4() which worked exactly the same.
I had not exactly the same problem, but something similar.
I was working with pre-Postgres13 version (so I did not have any function ready).
I had a table where I needed to insert new rows into the table while generating a new UUID(v4) for each new row.
I was looking everywhere but couldn't find anything w/o creating a function or installing extensions.
So I made it this way:
INSERT INTO monitors_nontest (id, col1, col2, col3)
SELECT uuid_in(md5(random()::text || random()::text)::cstring), mn.col1, mn.col2, mn.col3
FROM monitors_nontest mn
WHERE mn.col1 = 'some-text'
This could be adjusted for the UPDATE query. I hope it will help somebody else.

IF... ELSE... two mutually exclusive inserts INTO #temptable

I need to insert either set A or set B of records into a #temptable, depending on certain condition
My pseudo-code:
IF OBJECT_ID('tempdb..#t1') IS NOT NULL DROP TABLE #t1;
IF {some-condition}
SELECT {columns}
INTO #t1
FROM {some-big-table}
WHERE {some-filter}
ELSE
SELECT {columns}
INTO #t1
FROM {some-other-big-table}
WHERE {some-other-filter}
The two SELECTs above are exclusive (guaranteed by the ELSE operator). However, SQL compiler tries to outsmart me and throws the following message:
There is already an object named '#t1' in the database.
My idea of "fixing" this is to create #t1 upfront and then executing a simple INSERT INTO (instead of SELECT... INTO). But I like minimalism and am wondering whether this can be achieved in an easier way i.e. without explicit CREATE TABLE #t1 upfront.
Btw why is it NOT giving me an error on a conditional DROP TABLE in the first line? Just wondering.
You can't have 2 temp tables with the same name in a single SQL batch. One of the MSDN article says "If more than one temporary table is created inside a single stored procedure or batch, they must have different names". You can have this logic with 2 different temp tables or table variable/temp table declared outside the IF-Else block.
Using a Dyamic sql we can handle this situation. As a developoer its not a good practice. Best to use table variable or temp table.
IF 1=2
BEGIN
EXEC ('SELECT 1 ID INTO #TEMP1
SELECT * FROM #TEMP1
')
END
ELSE
EXEC ('SELECT 2 ID INTO #TEMP1
SELECT * FROM #TEMP1
')

How to insert JPEG into a SQL Server 2000 database field of image type using Transact SQL

I'm trying to figure out how to insert a .JPG file into a SQL Server 2000 database field of type image using Transact SQL. Thanks.
Use OPENROWSET:
INSERT MyTable (ImageColumnName)
SELECT BulkColumn FROM OPENROWSET (BULK 'c:\myjpeg.jpg', SINGLE_BLOB) AS X
EDITED Whoops, you're using 2000--the previous solution is not supported. You have to use WRITETEXT:
CREATE TABLE MyTable
(
ID INT PRIMARY KEY IDENTITY (1,1),
ImageColumnName IMAGE NULL
)
GO
-- must insert a dummy value into the image column for TEXTPTR
-- to work in next bit
DECLARE #RowId INT
INSERT MyTable (ImageColumnName) VALUES (0xFFFFFFFF)
SELECT #RowId = SCOPE_IDENTITY()
-- get a pointer value to the row+column you want to
-- write the image to
DECLARE #Pointer_Value varbinary(16)
SELECT #Pointer_Value = TEXTPTR(ImageColumnName)
FROM MyTable
WHERE Id = #RowId
-- write the image to the row+column pointer
WRITETEXT MyTable.ImageColumnName #Pointer_Value 'c:\myjpeg.jpg'
There is a tool called textcopy.exe
You can find it under MSSQL\Binn or get it with SQL Server 2000 SP4
Alexander Chigrik wrote a nice stored procedure for usinig it with SQL query:
http://www.mssqlcity.com/Articles/KnowHow/Textcopy.htm
The stored procedure found in this tutorial worked for me:
Brief tutorial on text, ntext, and image

Return the id of the just added row

In a similar vein to my previous question I again ask the SO guys for your collective wisdom and help.
In a stored procedure and after passing some checks I need to insert a new row and return the newly created id for it. The check if a row exists works so it is the bit after that which I am undecided upon.
The table has two important columns: The LocationID and the CaseID. The CaseID is autoincrementing, so when you add insert a new locationid it will automatically rachet up.
I currently have this:
-- previous checks for existance of CaseID
IF #CaseID IS NULL
BEGIN
INSERT INTO
Cases(LocationID)
VALUES
(#LocationID)
-- what now?
END
I was thinking of performing a #CaseID = (SELECT blah) statement immeadiately after but I was wondering if there is a better way?
Is there a better way? How would you do this?
SELECT #CaseID = SCOPE_IDENTITY()
In fact, you can just do (if that's the end of the stored proc.):
SELECT SCOPE_IDENTITY()
(The OUTPUT clause is only available in SQL Server 2005 onwards...)
Ref: SCOPE_IDENTITY
scope_identity()
SELECT SCOPE_IDENTITY()
As others mentioned, SCOPE_IDENTITY() is the way to go, though some ORM tools provide this functionality as well.
The only thing you need to remember is SCOPE_IDENTITY() will return the last identity key value generated during the current session only. This is useful in filtering out new keys which may have been created by other clients simultaneously. SELECT ##IDENTITY will return the last key generated by any client/session.
You need to use the OUTPUT clause
http://blog.jemm.net/articles/databases/how-to-using-sql-server-2005s-output-to-return-generated-identity/
...which, as pointed out, is only available in sqlserver 2005. Plz disregard.

Sequence Generators in T-SQL

We have an Oracle application that uses a standard pattern to populate surrogate keys. We have a series of extrinsic rows (that have specific values for the surrogate keys) and other rows that have intrinsic values.
We use the following Oracle trigger snippet to determine what to do with the Surrogate key on insert:
IF :NEW.SurrogateKey IS NULL THEN
SELECT SurrogateKey_SEQ.NEXTVAL INTO :NEW.SurrogateKey FROM DUAL;
END IF;
If the supplied surrogate key is null then get a value from the nominated sequence, else pass the supplied surrogate key through to the row.
I can't seem to find an easy way to do this is T-SQL. There are all sorts of approaches, but none of which use the notion of a sequence generator like Oracle and other SQL-92 compliant DBs do.
Anybody know of a really efficient way to do this in SQL Server T-SQL? By the way, we're using SQL Server 2008 if that's any help.
You may want to look at IDENTITY. This gives you a column for which the value will be determined when you insert the row.
This may mean that you have to insert the row, and determine the value afterwards, using SCOPE_IDENTITY().
There is also an article on simulating Oracle Sequences in SQL Server here: http://www.sqlmag.com/Articles/ArticleID/46900/46900.html?Ad=1
Identity is one approach, although it will generate unique identifiers at a per table level.
Another approach is to use unique identifiers, in particualr using NewSequantialID() that ensues the generated id is always bigger than the last. The problem with this approach is you are no longer dealing with integers.
The closest way to emulate the oracle method is to have a separate table with a counter field, and then write a user defined function that queries this field, increments it, and returns the value.
Here is a way to do it using a table to store your last sequence number. The stored proc is very simple, most of the stuff in there is because I'm lazy and don't like surprises should I forget something so...here it is:
----- Create the sequence value table.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[SequenceTbl]
(
[CurrentValue] [bigint]
) ON [PRIMARY]
GO
-----------------Create the stored procedure
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[sp_NextInSequence](#SkipCount BigInt = 1)
AS
BEGIN
BEGIN TRANSACTION
DECLARE #NextInSequence BigInt;
IF NOT EXISTS
(
SELECT
CurrentValue
FROM
SequenceTbl
)
INSERT INTO SequenceTbl (CurrentValue) VALUES (0);
SELECT TOP 1
#NextInSequence = ISNULL(CurrentValue, 0) + 1
FROM
SequenceTbl WITH (HoldLock);
UPDATE SequenceTbl WITH (UPDLOCK)
SET CurrentValue = #NextInSequence + (#SkipCount - 1);
COMMIT TRANSACTION
RETURN #NextInSequence
END;
GO
--------Use the stored procedure in Sql Manager to retrive a test value.
declare #NextInSequence BigInt
exec #NextInSequence = sp_NextInSequence;
--exec #NextInSequence = sp_NextInSequence <skipcount>;
select NextInSequence = #NextInSequence;
-----Show the current table value.
select * from SequenceTbl;
The astute will notice that there is a parameter (optional) for the stored proc. This is to allow the caller to reserve a block of ID's in the instance that the caller has more than one record that needs a unique id - using the SkipCount, the caller need make only a single call for however many IDs are needed.
The entire "IF EXISTS...INSERT INTO..." block can be removed if you remember to insert a record when the table is created. If you also remember to insert that record with a value (your seed value - a number which will never be used as an ID), you can also remove the ISNULL(...) portion of the select and just use CurrentValue + 1.
Now, before anyone makes a comment, please note that I am a software engineer, not a dba! So, any constructive criticism concerning the use of "Top 1", "With (HoldLock)" and "With (UPDLock)" is welcome. I don't know how well this will scale but this works OK for me so far...