DB2 9.7 SQL syntax, what am I doing wrong? - sql

For one reason or other which are out of my control I am attempting to simply pull data over the past 12 months. However, essentially down to the size of data, I have to query each day into a temp table and go from there. Now I'm a newbie to scripting in DB2, but not SQL in general, so I've tried the code below (the logic seems fine to me).
Initially I'm was just interested in how many records will be generated, but ideally I'd want to run the second SELECT code. I've been using Data Studio, but I believe to export the data I would need to run this via CLP. Now my issues are that I'm missing something within syntax that I can't figure out and it's doing my head in. It's most likely something very basic or I'm just doing it totally wrong.
If I try the SELECT COUNT(*) code I'm getting these errors: -
Multiple markers at this line
- DB2 for Linux, UNIX, and Windows: "" was expected after "FROM".
- DB2 for Linux, UNIX, and Windows: "." is invalid.
If I try the other SELECT code I'm getting these errors: -
DB2 for Linux, UNIX, and Windows: "" was expected instead of "SELECT MARKET_ID,
SUPER_REGION,
REGION,
MARKET,
POSA,
DEST_ID,
DEST_NAME,
DEST_TYPE,
STAT...
DB2 for Linux, UNIX, and Windows: "," was expected after "FROM".
DB2 for Linux, UNIX, and Windows: "," was expected after "GROUP"
I just don't get it. Can someone please help? The SESSION.l12_Dest table also doesn't seem to be available afterwards for me to try looking at the table manually.
Code: -
--<ScriptOptions statementTerminator="#"/>
CREATE OR REPLACE PROCEDURE HWW.DM_CHECKLIST()
LANGUAGE SQL
BEGIN
DECLARE GLOBAL TEMPORARY TABLE SESSION.L12_DEST
(
ACTUAL_DATE DATE,
MARKET_ID INTEGER,
SUPER_REGION VARCHAR (100),
REGION VARCHAR (100),
MARKET VARCHAR (100),
POSA VARCHAR (100),
DEST_ID INTEGER,
DEST_NAME VARCHAR (100),
DEST_TYPE VARCHAR (30),
STATUS_CODE SMALLINT,
VISITORS INTEGER
)
ON COMMIT PRESERVE ROWS NOT LOGGED;
COMMIT;
FOR V_ROW AS
SELECT ACTUAL_DATE
FROM DM.DATE_DIM
WHERE ACTUAL_DATE
BETWEEN (CURRENT_DATE - 12 MONTHS) - DAY((CURRENT_DATE - 12 MONTHS)) DAYS + 1 DAYS
AND LAST_DAY((CURRENT_DATE - 1 MONTHS))
DO
INSERT INTO SESSION.L12_DEST
SELECT B.ACTUAL_DATE,
Z.HCOM_SRCH_DEST_PROPERTY_MKT_ID,
Z.HCOM_SRCH_DEST_PROPERTY_MKT_SUPER_REGN_NAME,
Z.HCOM_SRCH_DEST_PROPERTY_MKT_REGN_NAME,
Z.HCOM_SRCH_DEST_PROPERTY_MKT_NAME,
S.SITE_CNTRY_NAME,
Z.HCOM_SRCH_DEST_ID,
Z.HCOM_SRCH_DEST_NAME,
Z.HCOM_SRCH_DEST_TYP_NAME,
LZ.STATUS_CODE,
COUNT(DISTINCT(F.VISITOR_KEY))AS VISITORS
FROM DM.LODG_DEMAND_FACT F
INNER JOIN DM.V_HCOM_SRCH_DEST_DIM Z
ON F.HCOM_SRCH_DEST_KEY = Z.HCOM_SRCH_DEST_KEY
INNER JOIN DM.DATE_DIM B
ON F.LOCAL_DEMAND_DATE_KEY = B.DATE_KEY
INNER JOIN DM.SITE_DIM S
ON S.SITE_KEY = F.SITE_KEY
LEFT JOIN LZ.LZ_HCM_DESTINATION LZ
ON Z.HCOM_SRCH_DEST_ID = LZ.DESTINATION_INT_ID
WHERE F.BRAND_KEY = 101
AND B.ACTUAL_DATE = V_ROW.ACTUAL_DATE
GROUP BY B.ACTUAL_DATE,
Z.HCOM_SRCH_DEST_PROPERTY_MKT_ID,
Z.HCOM_SRCH_DEST_PROPERTY_MKT_SUPER_REGN_NAME,
Z.HCOM_SRCH_DEST_PROPERTY_MKT_REGN_NAME,
Z.HCOM_SRCH_DEST_PROPERTY_MKT_NAME,
S.SITE_CNTRY_NAME,
Z.HCOM_SRCH_DEST_ID,
Z.HCOM_SRCH_DEST_NAME,
Z.HCOM_SRCH_DEST_TYP_NAME,
LZ.STATUS_CODE;
END FOR;
--SELECT COUNT(*) FROM SESSION.L12_DEST;
--EXPORT TO C:\TEMP\MARKETS.TXT OF DEL
SELECT MARKET_ID,
SUPER_REGION,
REGION,
MARKET,
POSA,
DEST_ID,
DEST_NAME,
DEST_TYPE,
STATUS_CODE,
SUM(VISITORS)
FROM SESSION.L12_DEST
GROUP BY MARKET_ID,
SUPER_REGION,
REGION,
MARKET,
POSA,
DEST_ID,
DEST_NAME,
DEST_TYPE,
STATUS_CODE;
END #

I don't think you can do a bare select statement in a stored proc (at least in DB2).
When I try this I get the same results: the most unhelpful error message ever. (thanks IBM)
But, if I put some valid statement after the END FOR; there isn't a problem.
If you want a stored proc to do a select and show you the answer do something like this:
CREATE OR REPLACE PROCEDURE DM_CHECKLIST()
LANGUAGE SQL
DYNAMIC RESULT SETS 1
BEGIN
DECLARE RS CURSOR FOR SELECT COUNT(*) FROM sysibm.sysdummy1;
OPEN RS;
END#
The interesting thing is the DYNAMIC RESULT SETS 1 business. That tells the executor that we will have a result set returned and to show it in the command line interface (cli).
You can have as many result sets as you like. You will see the results on the cli, or you can get them pragmatically.
I did try the EXPORT command and it didn't seem to work.
EXPORT TO "C:\TEMP\MARKETS.TXT" OF DEL SELECT COUNT(*) FROM sysibm.sysdummy1;
It looks like you aren't allowed to use EXPORT in a stored proc, as it is a DB2 command and not a SQL statement. I wouldn't be surprised if there is some way to do it, but not this way.

To use the EXPORT utility in a stored procedure, you have to wrap it in the ADMIN_CMD procedure:
create procedure dm_checklist()
language sql
begin
declare global temporary table results (...)
on commit preserve rows;
-- Steps to build / populate your temporary table
call sysproc.admin_cmd('export to C:\file.del of del select * from session.results');
end

Related

Delphi XE: TSQLQuery raises "Input parameter mismatch"

I've created a stored procedure (SP_TEST) in Firebird 2.5 that returns a selection and uses 3 input parameters.
This SP_TEST works fine in IBExpress, when I enter e.g. the SQL command: select * from SP_TEST(1,2,3) there's a correct result.
However, when I use the dbExpress components in Delphi XE to do the same selection, the message "Input parameter mismatch" is raised.
I'm using the TSQLQuery component, with the CommandText property set to "select * from SP_TEST(1,2,3)".
When I define the same query in the CommandText property as I've defined in SP_TEST, I have a nice result.
But including the stored procedure name in the select statement (with the right number of parameter) raises this error message.
Can anyone please tell me what I'm doing wrong?
UPDATE - moved from pseudo-answer and formatting added: https://stackoverflow.com/editing-help
SET TERM ^ ;
ALTER PROCEDURE SP_TEST (
NJAAR Integer,
NMAAND Integer,
CREKNR Varchar(6) )
RETURNS (
TOTAAL Numeric(15,2),
CODE Varchar(4),
OMSCHR Varchar(40),
RICHTING Varchar(3),
PROG Numeric(18,2),
TRANS Numeric(18,2) )
AS
BEGIN
FOR
SELECT a.CODE, a.OMSCHR, a.RICHTING,
SUM(iif(richting='AF',-1,1) * COALESCE(p.bedrag,0)) as prog,
SUM(iif(richting='AF',-1,1) * COALESCE(t.bedrag,0)) as trans,
0 as totaal
FROM POSTEN a
left JOIN prognose p on p.POST = a.CODE and p.JAAR = :nJAAR and p.MAAND = :nMAAND
left JOIN TRANSACT t on t.POST = a.CODE and EXTRACT(YEAR FROM t.datum) = :nJAAR and EXTRACT(MONTH FROM t.datum) = :nMAAND
WHERE REKTOEPAS containing :cREKNR
GROUP by a.CODE, a.OMSCHR, a.RICHTING
INTO :CODE,
:OMSCHR,
:RICHTING,
:PROG,
:TRANS,
:TOTAAL
DO
BEGIN
totaal = iif(abs(prog) > abs(trans),prog,trans);
SUSPEND;
END
END^
SET TERM ; ^
Entering the statement in IBExpert:
SELECT * FROM SP_TEST(2014,1,'0001')
returns a result set of several rows.
When I pass the same query to TSQLQuery.CommandText (or to the SQL property, I also tried this), the message "Input parameter mismatch" is raised.
I tried TSQLStoredProc, but then there's a message "Cursor not returned from Query" when I use the Open method.
In fact, my stored procedure is kind of a view, and I like the result in my dbExpress components (TSQLQuery or TSQLStoredProc).
As said, when I put the complete SQL code between "SELECT..." and the "GROUP BY" in the TSQLQuery.SQL propery, I get the resulting rows. Using the stored procedure fails until now.
Your third parameter is a VARCHAR, but you try to use select * from SP_TEST(1,2,3) and here the third argument is an INTEGER. This is also the only difference with the procedure call that does work.
So instead use:
select * from SP_TEST(1,2,'3')
(note the quotes around the 3)

Declaring and Setting Variables of Different Data Types in Oracle SQL

I am currently writing a SQL query which first creates a lot of temporary tables using the WITH operator along with SELECT statements and then joins all of the temporary statements at the end.
All of my SELECT statements that create temporary tables depend on certain filters... so my query looks something liek
WITH
table_1 as (
SELECT product_id
avg(price)
FROM daily_sales
WHERE product_category = 1
AND sell_date BETWEEN TO_DATE('2012/01/07','YYYY/DD/MM') AND TO_DATE('2012/30/09','YYYY/DD/MM')
GROUP BY ds.product_id
),
table_2 as (....
),
SELECT FROM table_1 JOIN table_2....
I would like to run this query for ranges of 'sell_date' (a date, or a string) and different values of 'product_category' (an integer value).
Currently, I am replacing these manually but I am wondering if I can just declare replace these hard-coded values with variables, which I set at the top of my query.
I understand that this might have been asked before - but I am confused since there are multiple solutions that depend on the exact version of SQL that you are using and the types of variables that you are declaring.
In this case, I am looking for a solution that works in Oracle SQL, and where I can specify the type variable.
It depends how you're running your query.
If you're using an interactive client like SQL*Plus or TOAD you should use substitution variables:
WITH
table_1 as (
SELECT product_id
avg(price)
FROM daily_sales
WHERE product_category = &product_cat
AND sell_date BETWEEN TO_DATE('&start_date','YYYY/DD/MM') AND TO_DATE('&end_date','YYYY/DD/MM')
GROUP BY ds.product_id
),
You will be prompted to supply values for these variables each time you run the query. If you want to use the same values in multiple places then declare all the occurrences of a variable with a double ampersand - &&product_category - and then you only be prompted for it once. The SQL*Plus documentation has additional information: find out more.
If you're going to run the queries in a stored procedure then define the values as parameters ...
procedure process_sales_details
( i_product_category in number
, i_start_date in date
, i_end_date in date )
... which you reference in your query (wherever you declare it) ...
WITH
table_1 as (
SELECT product_id
avg(price)
FROM daily_sales
WHERE product_category = i_product_cat
AND sell_date BETWEEN i_start_date AND i_end_date
GROUP BY ds.product_id
),
Further to APC's answer, in SQL*Plus or SQL Developer you can also declare variables that you can assign values to in an anonymous PL/SQL block and then reference as bind variables in your plain SQL query:
variable v_product_cat number;
variable v_start_date varchar2(10);
variable v_end_date varchar2(10);
begin
:v_product_cat := 1;
:v_start_date := '2012/01/07';
:v_end_date := '2012/30/09';
end;
/
WITH table_1 as (
SELECT product_id
avg(price)
from daily_sales
where product_category = :v_product_cat
AND sell_date BETWEEN TO_DATE(:v_start_date,'YYYY/DD/MM')
AND TO_DATE(:v_end_date,'YYYY/DD/MM')
group by ds.product_id
)
...
Note the : before the variable name denoting a bind variable, and that the strings are not enclosed in quotes with this form. Unfortunately you can't declare a date variable, which would make this even neater.
And if you use substitution variables you can define them at the start so you aren't prompted; in this case you don't need to use the && notation either:
define v_product_cat=1
define v_start_date=2012/01/07
define v_end_date=2012/30/09
...
where product_category = &v_product_cat
and sell_date between to_date('&v_start_date','YYYY/DD/MM')
AND TO_DATE('&v_end_date','YYYY/DD/MM')
...
... which is covered in the documentation APC linked to.
You can add one or more common table expressions to encapsulate these:
with
cte_sell_dates as (
select date '2012-07-01' from_date,
date '2012-09-30' to_date
from dual),
cte_products as (
select 1 id from dual union all
select 28 id from dual),
... as (
select ...
from ...
where sell_date between (select from_date from cte_sell_dates) and
(select to_date from cte_sell_dates) and
product_id in (select id from cte_products )
...
... or use joins directly, instead of subqueries, of course.
Go for an Anonymous PL/sql Block and use a for loop where you can loop through all the different values.
Following is the Structure of pl/sql block:
DECLARE
<constant name> CONSTANT <data type> := <value>;
<constant name> CONSTANT <data type> DEFAULT <value>;
BEGIN
<valid statement>;
EXCEPTION
<exception handler>;
END;
Also you can go for a parametrized cursor where you can pass your values.

BLOB to String, SQL Server

I have a text string stored as a BLOB data type in a database. I want to extract it by an SQL select query, but I have problems converting/casting from BLOB to readable text.
I've tried e.g.
select convert(nvarchar(40),convert(varbinary(40),BLOBTextToExtract))
from [NavisionSQL$Customer]
I guess I need something similar, but I can't figure out exactly what I need to do the conversion. Can somebody please give me some directions?
Regards
The accepted answer works for me only for the first 30 characters.
This works for me:
select convert(varchar(max), convert(varbinary(max),myBlobColumn)) FROM table_name
Problem was apparently not the SQL server, but the NAV system that updates the field. There is a compression property that can be used on BLOB fields in NAV, that is not a part of SQL Server. So the custom compression made the data unreadable, though the conversion worked.
The solution was to turn off compression through the Object Designer, Table Designer, Properties for the field (Shift+F4 on the field row).
After that the extraction of data can be made with e.g.:
select convert(varchar(max), cast(BLOBFIELD as binary))
from Table
Thanks for all answers that were correct in many ways!
It depends on how the data was initially put into the column. Try either of these as one should work:
SELECT CONVERT(NVarChar(40), BLOBTextToExtract)
FROM [NavisionSQL$Customer];
Or if it was just varchar...
SELECT CONVERT(VarChar(40), BLOBTextToExtract)
FROM [NavisionSQL$Customer];
I used this script to verify and test on SQL Server 2K8 R2:
DECLARE #blob VarBinary(MAX) = CONVERT(VarBinary(MAX), 'test');
-- show the binary representation
SELECT #blob;
-- this doesn't work
SELECT CONVERT(NVarChar(100), #blob);
-- but this does
SELECT CONVERT(VarChar(100), #blob);
Can you try this:
select convert(nvarchar(max),convert(varbinary(max),blob_column)) from table_name
Found this...
bcp "SELECT top 1 BlobText FROM TableName" queryout "C:\DesinationFolder\FileName.txt" -T -c'
If you need to know about different options of bcp flags...
http://msdn.microsoft.com/en-us/library/ms162802.aspx
CREATE OR REPLACE FUNCTION HASTANE.getXXXXX(p_rowid in rowid) return VARCHAR2
as
l_data long;
begin
select XXXXXX into l_data from XXXXX where rowid = p_rowid;
return substr( l_data, 1, 4000);
end getlabrapor1;

SQL syntax as parameters for a MySQL Routine?

I have the following MySQL routine:
DELIMITER $$
CREATE DEFINER=`root`#`%` PROCEDURE `getGroupOrders`(grp INT,
ord CHAR(20),
srt CHAR(4),
page INT,
count INT)
BEGIN
SELECT *
FROM `dbre`.`order_info`
WHERE username IN (SELECT `dbre`.`users`.`username`
FROM `dbre`.`users`
WHERE `dbre`.`users`.`id_group` = grp)
ORDER BY ord srt LIMIT page,count;
END
As you can see, I want to pass the ordering column and sorting as a parameters, however I get a syntax error is there a way to do this or do I have to make similar routines for each type of ordering?
I don't think this is possible in the way you try it.
You cannot use a variable to define the ORDER BY column an direction.
The only workaround I can think of is to create a prepared statement from a dynamically created string (where you can use the variables to specify the order by details) and then execute that prepared statement.
Here is an example of such a dynamic statement:
http://forums.mysql.com/read.php?98,393613,393642#msg-393642

Informix: Select null problem

Using Informix, I've created a tempory table which I am trying to populate from a select statement. After this, I want to do an update, to populate more fields in the tempory table.
So I'm doing something like;
create temp table _results (group_ser int, item_ser int, restype char(4));
insert into _results (group_ser, item_ser)
select
group_ser, item_ser, null
from
sometable
But you can't select null.
For example;
select first 1 current from systables
works but
select first 1 null from systables
fails!
(Don't get me started on why I can't just do a SQL Server like "select current" with no table specified!)
You don't have to write a stored procedure; you simply have to tell IDS what type the NULL is. Assuming you are not using IDS 7.31 (which does not support any cast notation), you can write:
SELECT NULL::INTEGER FROM dual;
SELECT CAST(NULL AS INTEGER) FROM dual;
And, if you don't have dual as a table (you probably don't), you can do one of a few things:
CREATE SYNONYM dual FOR sysmaster:"informix".sysdual;
The 'sysdual' table was added relatively recently (IDS 11.10, IIRC), so if you are using an older version, it won't exist. The following works with any version of IDS - it's what I use.
-- #(#)$Id: dual.sql,v 2.1 2004/11/01 18:16:32 jleffler Exp $
-- Create table DUAL - structurally equivalent to Oracle's similarly named table.
-- It contains one row of data.
CREATE TABLE dual
(
dummy CHAR(1) DEFAULT 'x' NOT NULL CHECK (dummy = 'x') PRIMARY KEY
) EXTENT SIZE 8 NEXT SIZE 8;
INSERT INTO dual VALUES('x');
REVOKE ALL ON dual FROM PUBLIC;
GRANT SELECT ON dual TO PUBLIC;
Idiomatically, if you are going to SELECT from Systables to get a single row, you should include 'WHERE tabid = 1'; this is the entry for Systables itself, and if it is missing, the fact that your SELECT statement does return any data is the least of your troubles. (I've never seen that as an error, though.)
This page says the reason you can't do that is because "NULL" doesn't have a type. So, the workaround is to create a sproc that simply returns NULL in the type you want.
That sounds like a pretty bad solution to me though. Maybe you could create a variable in your script, set it to null, then select that variable instead? Something like this:
DEFINE dummy INT;
LET dummy = NULL;
SELECT group_ser, item_ser, dummy
FROM sometable
SELECT group_ser, item_ser, replace(null,null) as my_null_column
FROM sometable
or you can use nvl(null,null) to return a null for your select statement.
Is there any reason to go for an actual table? I have been using
select blah from table(set{1})
select blah from table(set{1})
is nice when you are using 10.x database. This statement doesn't touch database. The amount of read/write operations is equal to 0,
but
when you're using 11.x it will cost you at least 4500 buffer reads because this version of Informix creates this table in memory and executes query against it.
select to_date(null) from table;
This works when I want to get a date with null value
You can use this expression (''+1) on the SELECT list, instead of null keyword. It evaluates to NULL value of type DECIMAL(2,0).
This (''+1.0001) evaluates to DECIMAL(16,4). And so on.
If you want DATE type use DATE(''+1) to get null value of type DATE.
(''+1)||' ' evaluates to an empty string of type VARCHAR(1).
To obtain NULL value of type VARCHAR(1) use this expression:
DATE(''+1)||' '
Works in 9.x and 11.x.