Don't understand 'CREATE VIEW' must be the first statement in a query batch - tsql

I am trying to create a View for temporary data I only need during the SQL run.
I get the error:
"'CREATE VIEW' must be the first statement in a query batch."
I searched on that and see it was asked about on Stack Overflow already here: 'CREATE VIEW' must be the first statement in a query batch
However, my SQL code looks like it should but I still get the error. First, I create a table called #StatusTable but that table wasn't working like I wanted. So I tried creating a View.
DECLARE #DOC INT;
DECLARE #StatusTable TABLE
(
status VARCHAR(8000)
)
INSERT INTO #StatusTable
Select Status = ( select CONCAT(ls.ItemText, '/', st.ItemText ) as Status from Inmate_Legal_Status ils
Left join jt_list_items ls on ls.ItemID = ils.LegalStatusID
Left join jt_list_items st on st.ItemID = ils.StatusTypeId
where ArrestNo = #doc and ils.RecordDeleted = 0
order by ils.CreatedTime desc FOR XML PATH('')
)
-- table
USE #StatusTable;
GO
create view MyView as
Select Status = ( select CONCAT(ls.ItemText, '/', st.ItemText ) as Status from Inmate_Legal_Status ils
Left join jt_list_items ls on ls.ItemID = ils.LegalStatusID
Left join jt_list_items st on st.ItemID = ils.StatusTypeId
where ArrestNo = #doc and ils.RecordDeleted = 0
GO
I get three errors:
Incorrect syntax near GO.
'CREATE VIEW' must be the first statement in a query batch
Incorrect syntax near #StatusTable

Related

Errors with declared array/table variable values in SAP HanaDB SQL

I am trying to add a declared variable to replace a hardcoded list of values in a "where in" clause.
Researching how Hana handles array variables it seems like I can do this by declaring an array and then either using a select directly on it or by unnesting it first into a table but I keep getting errors I can't resolve.
When I try it this way:
DO
BEGIN
DECLARE CODES_ARRAY NVARCHAR(100) ARRAY = ARRAY('01','02','03','04');
SELECT T0."ItemCode"
FROM OITM T0
INNER JOIN OITW T1 ON T0."ItemCode" = T1."ItemCode"
WHERE "WhsCode" IN (SELECT "code" FROM :CODES_ARRAY); -- line 9 where error occurs
END;
I get this error message sql syntax error: incorrect syntax near ")": line 9 col 54 (at pos 239)
I can't figure out what the syntax error resolution is.
So then I tried inserting a declared table variable like this:
DO
BEGIN
DECLARE CODES_ARRAY NVARCHAR(100) ARRAY = ARRAY('01','02','03','04');
DECLARE CODES_TABLE TABLE = UNNEST(:CODES_ARRAY) AS ("code"); -- line 5 where error occurs
SELECT T0."ItemCode"
FROM OITM T0
INNER JOIN OITW T1 ON T0."ItemCode" = T1."ItemCode"
WHERE "WhsCode" IN (SELECT "code" FROM CODES_TABLE); -- I know : is missing here but when adding, the same error from previous block shows up
END;
and I get this error message: identifier must be declared: 1: line 5 col 38 (at pos 123)
As far as I can tell the array variable is declared as it should be so I don't know how to resolve the error.
I've read the SAP Hana SQL Reference documentation (for array/table variables, unnest function, etc.) over and over and it seems like I've got everything setup correctly but can't figure out these errors. I would like to be able to use both of these approaches at different times if possible (the "array variable to table variable" and the "array variable only" approaches)
I don't know exactly what is going on here, but one thing I notice that the two different error messages referenced in my post (see difference from errors in the first two code blocks) is that each error is occurring either immediately before the use of the variable with the : (in the case of the UNNEST) or immediately following the variable with the : (in the case of using in the SELECT * FROM in the query).
Because of that, I wondered if the issue is "upstream" at the Hana ADO.NET application query preparation and execution call level, but I ran a test and when I double checked the query string just before it is executed, it appears unchanged and the variables with : still look as they should, so at least as far as just before execution through Hana ADO.NET HanaCommand it looks correct - but once executing the query using HanaDataReader or HanaDataAdapter it returns the error messages referred to above. It may be a red herring to chase the problem from the Hana ADO.NET level but don't know what else to do.
Update
To further troubleshoot, I tried executing this code block below using hdbsql.exe -n XXX.XXX.XXX.XXX:30015 -u XXX -p XXX -m -I "c:\temp\test.sql" -c "#" and it works! So, the errors I see only show up when executing the same query through the Hana ADO.NET interface.
DO
BEGIN
DECLARE CODES_ARRAY NVARCHAR(10) ARRAY = ARRAY('01','02','03','04');
DECLARE CODES_TABLE TABLE ("code" NVARCHAR(10)) = UNNEST(:CODES_ARRAY) AS ("code");
SELECT T0."ItemCode"
FROM OITM T0
INNER JOIN OITW T1 ON T0."ItemCode" = T1."ItemCode"
WHERE "WhsCode" IN (SELECT "code" FROM :CODES_TABLE); -- line 10 where error occurs when using Hana ADO.NET
END;
#
The above fails when through Hana ADO.NET with the error message: sql syntax error: incorrect syntax near ")": line 10 col 54 (at pos 325) but works when executed through hdbsql.
Update
The C# code that executes the query is fairly straight forward, but for completeness of troubleshooting effort I am including the interesting parts of our HanaHelper class. This code works successfully to execute 100s of queries a day without errors or problems. This is the first time where a variable of any type has been attempted to be declared or used in a query through this code and when the errors started showing up. As far as I can tell, the issue is tied to the use of the : when using the variable in the query.
public class HanaHelper
{
public HanaConnection objConn = null;
public HanaHelper(string ConnectionString)
{
try
{
objConn = new HanaConnection(ConnectionString);
}
catch (Exception e)
{
Console.WriteLine(#"Exception thrown by HanaConnection: {0}\n{1}", e.Message, e.InnerException);
}
}
public DataSet GetData(string strSQL)
{
using (HanaCommand objCmd = new HanaCommand(strSQL, objConn))
{
using (HanaDataAdapter objDA = new HanaDataAdapter(objCmd))
{
DataSet objDS = new DataSet();
try
{
objDA.Fill(objDS);
}
catch (Exception)
{
throw;
}
finally
{
// do something interesting regardless of success or failure
}
objConn.Close();
return objDS;
}
}
}
}
Any clue here why the same query works through hdbsql but fails when executing through Hana ADO.NET?
Update
I figured out how to use HanaSQLTrace in the C# code so that I can inspect the prepared query text and viola, the source of error messages becomes apparent, all occurrences of ":VARNAME" are replaced with "? " (a ? replaces the : and a space for each character in the variable name). I suppose it is trying to pre-substitute occurrences of : with a ? as if there were parameters to be substituted.
How can this behavior be disabled, or worked with, or worked around so that I can use variables in a query in Hana ADO.NET effectively?
Updated based on the OP feedback.
To refer to a variable (in order to access its value(s)) in SQLScript it's
necessary to put a colon : in front of the variable name.
The main issue, however, turns out to be the declaration of the CODES_TABLE table variable.
With HANA 2 SPS 4 the error message is
`SAP DBTech JDBC: [264]: invalid datatype: unknown type SYSTEM.TABLE: line 5 col 23`
This points to the declaration of the TABLE typed variable CODES_TABLE which lacks the definition of what columns should be in the table.
Adding this fixes the issue.
With these changes, your code should work:
DO
BEGIN
DECLARE CODES_ARRAY NVARCHAR(100) ARRAY = ARRAY('01','02','03','04');
DECLARE CODES_TABLE TABLE ("code" NVARCHAR(100)) = UNNEST(:CODES_ARRAY) AS ("code");
-- ^^^^^^^^^^^^^^^^^^^^^^
-- |
---------------------------------------+
SELECT
T0."ItemCode"
FROM
OITM T0
INNER JOIN OITW T1
ON T0."ItemCode" = T1."ItemCode"
WHERE
"WhsCode" IN (SELECT "code" FROM :CODES_TABLE);
-- ^
-- |
---------------------------------------+
END;
An alternative option to declare and assign the table variable is to not use the DECLARE command.
DO
BEGIN
DECLARE CODES_ARRAY NVARCHAR(100) ARRAY = ARRAY('01','02','03','04');
CODES_TABLE = UNNEST(:CODES_ARRAY) AS ("code");
SELECT
T0."ItemCode"
FROM
OITM T0
INNER JOIN OITW T1
ON T0."ItemCode" = T1."ItemCode"
WHERE
"WhsCode" IN (SELECT "code" FROM :CODES_TABLE);
END;

CASE, WHEN, THEN on UPDATE

I have this issue where if there is no record in copy_on_write.id then the UPDATE listings SET images = (SELECT images FROM new_vals) runs and wipes out listings.images with nothing.
So, I am trying to use a condition to only run the UPDATE listings if copy_on_write.images exist.
right now I get:
psql:queries/copy-to-source.sh:20: ERROR: syntax error at or near "CASE"
LINE 10: CASE WHEN images <>
WITH
new_vals AS (
SELECT *
FROM copy_on_write
WHERE copy_on_write.posted_by = 102550922::text
AND copy_on_write.id = 4
),
updates AS (
SELECT images FROM new_vals,
CASE WHEN images <> ''
THEN UPDATE listings SET images = (SELECT images FROM new_vals)
END
)
SELECT internal_id FROM new_vals
You can use updates CTE like this:
...
updates AS (
UPDATE listings SET
images = new_vals.images
FROM new_vals
WHERE new_vals.images <> ''
)
....
Note, that:
Your new_vals CTE should always return maximum one record, otherwise this won't works correct.
Also this not updates listings table, if new_vals returns images column, but it is empty string (or null). If in such cases you need run update anyway, then remove WHERE new_vals.images <> '' at all.
And also, this statement will update all listings.images records. Do you really want this?

PostgreSQL subquery split rows (error is more than one row returned by a subquery used as an expression)

this is my first post on stackoverflow so please be gentle. I have researched this problem and came up with many varied solutions...all of which seem to be just off from what I need. I have a postgresql subquery in a SELECT statement that returns multiple rows, which I know is obviously not ideal/allowed/sensible/etc.... However, this is the case for my current problem and I need to be able to take those multiple returned rows and apply them to every previous corresponding record row that they originally came out of.
Current Query:
SELECT cohead_number "Sales Order#",
cohead_custponumber "Purchase Order#",
item_number "Part Number",
item_descrip1 "Part Description",
CAST(shipitem_qty AS integer) "Item Quantity",
getPacklistItemLotSerial(shiphead_id, coitem_id) AS "LotNumbers" --this is the duplicating row subquery that I need listed in separate rows without changing other respective columns--
FROM cohead
LEFT JOIN coitem
ON coitem_cohead_id = cohead_id
LEFT JOIN shipitem
ON coitem_id = shipitem_orderitem_id
LEFT JOIN itemsite
ON coitem_itemsite_id = itemsite_id
LEFT JOIN item
ON itemsite_item_id = item_id
LEFT JOIN shiphead
ON shiphead_order_id = cohead_id
WHERE cohead_number = '79464' --this is just to test with one sales order instead of all (sales order being the input for the query)--
Results:
LINK: Results of above query here
What I Have Tried
Now, this line lets me split the column results via the delimiter ',' but I can't figure out how to get the results from this back into my original query's results:
(SELECT lot from regexp_split_to_table(getPacklistItemLotSerial(shiphead_id, coitem_id),', ') AS lot)
Results:
Here I input the shiphead_id and coitem_id for the example sales order so it can show you the resulting split column into rows.
SELECT lot from regexp_split_to_table(getPacklistItemLotSerial(22082, 50351),', ') AS lot
LINK: Results of Example
Please help walk me through what I need to do to achieve this. I imaging we need to declare some things and maybe join 2 tables in a more complex query...I don't really know. Thank you for any help you can offer.
EDIT
Added in the requested source code for the Function "getpacklistitemlotserial"
DECLARE
pShipheadId ALIAS FOR $1;
pOrderItemId ALIAS FOR $2;
_lotserial text;
_r RECORD;
_first BOOLEAN;
BEGIN
--Test to see if Lot/Serial Enabled
SELECT metric_value INTO _lotserial
FROM metric
WHERE ((metric_name='LotSerialControl')
AND (metric_value ='t'));
IF (FOUND) THEN
_lotserial := '';
_first := true;
FOR _r IN SELECT DISTINCT ls_number
FROM invdetail, invhist, shipitem, ls
WHERE ((shipitem_shiphead_id=pShipheadId)
AND (shipitem_orderitem_id=pOrderItemId)
AND (shipitem_invhist_id=invhist_id)
AND (invhist_id=invdetail_invhist_id)
AND (invdetail_ls_id=ls_id)) LOOP
IF (_first = false) THEN
_lotserial := _lotserial || ', ';
END IF;
_lotserial := _lotserial || _r.ls_number;
_first := false;
END LOOP;
RETURN _lotserial;
ELSE
RETURN '';
END IF;
END
Try:
SELECT DISTINCT cohead_number "Sales Order#",
cohead_custponumber "Purchase Order#",
item_number "Part Number",
item_descrip1 "Part Description",
CAST(shipitem_qty AS integer) "Item Quantity",
-- getPacklistItemLotSerial(shiphead.shiphead_id, coitem.coitem_id) AS "LotNumbers" --this is the duplicating row subquery that I need listed in separate rows without changing other respective columns--
CASE WHEN EXISTS(
SELECT metric_value FROM metric
WHERE metric_name='LotSerialControl' AND metric_value ='t'
)
THEN x.ls_number
ELSE ''
END AS "LotNumbers"
FROM cohead
LEFT JOIN coitem
ON coitem_cohead_id = cohead_id
LEFT JOIN shipitem
ON coitem_id = shipitem_orderitem_id
LEFT JOIN itemsite
ON coitem_itemsite_id = itemsite_id
LEFT JOIN item
ON itemsite_item_id = item_id
LEFT JOIN shiphead
ON shiphead_order_id = cohead_id
LEFT JOIN (
SELECT ls_number,
shipitem_shiphead_id, -- parameter: pShipheadId
shipitem_orderitem_id -- parameter pOrderItemId
FROM invdetail, invhist, shipitem, ls
WHERE
-- (shipitem_shiphead_id=pShipheadId)
-- AND (shipitem_orderitem_id=pOrderItemId)
(shipitem_invhist_id=invhist_id)
AND (invhist_id=invdetail_invhist_id)
AND (invdetail_ls_id=ls_id)
) x
ON ( x.shipitem_shiphead_id = shiphead.shiphead_id
AND
x.shipitem_orderitem_id = coitem.coitem_id
)
WHERE cohead_number = '79464'

DB2 Query : insert data in history table if not exists already

I have History table and transaction table.....and reference table...
If status in reference table is CLOSE then take those record verify in History table if not there insert from transaction table..... wiring query like this .... checking better one... please advice.. this query can be used for huge data ?
INSERT INTO LIB1.HIST_TBL
( SELECT R.ACCT, R.STATUS, R.DATE FROM
LIB2.HIST_TBL R JOIN LIB1.REF_TBL C
ON R.ACCT = C.ACCT WHERE C.STATUS = '5'
AND R.ACCT NOT IN
(SELECT ACTNO FROM LIB1.HIST_TBL)) ;
If you're on a current release of DB2 for i, take a look at the MERGE statement
MERGE INTO hist_tbl H
USING (SELECT * FROM ref_tbl R
WHERE r.status = 'S')
ON h.actno = r.actno
WHEN NOT MATCHED THEN
INSERT (actno,histcol2, histcol3) VALUES (r.actno,r.refcol2,r.refcol3)
--if needed
WHEN MATCHED
UPDATE SET (actno,histcol2, histcol3) = (r.actno,r.refcol2,r.refcol3)

Metadata about a column in SQL Server 2008 R2?

I'm trying to figure out a way to store metadata about a column without repeating myself.
I'm currently working on a generic dimension loading SSIS package that will handle all my dimensions. It currently does :
Create a temporary table identical to the given table name in parameters (this is a generic stored procedure that receive the table name as parameter, and then do : select top 0 * into ##[INSERT ORIGINAL TABLE NAME HERE] from [INSERT ORIGINAL TABLE NAME HERE]).
==> Here we insert custom code for this particular dimension that will first query the data from a datasource and get my delta, then transform the data and finally loads it into my temporary table.
Merge the temporary table into my original table with a T-SQL MERGE, taking care of type1 and type2 fields accordingly.
My problem right now is that I have to maintain a table with all the fields in it to store a metadata to tell my scripts if this particular field is type1 or type2... this is nonsense, I can get the same data (minus type1/type2) from sys.columns/sys.types.
I was ultimately thinking about renaming my fields to include their type in it, such as :
FirstName_T2, LastName_T2, Sex_T1 (well, I know this can be type2, let's not fall into that debate here).
What do you guyz would do with that? My solution (using a table with that metadata) is currently in place and working, but it's obvious that repeating myself from the systables to a custom table is nonsense, just for a simple type1/type2 info.
UPDATE: I also thought about creating user defined types like varchar => t1_varchar, t2_varchar, etc. This sounds like something a bit sluggy too...
Everything you need should already be in INFORMATION_SCHEMA.COLUMNS
I can't follow your thinking of not using provided tables/views...
Edit: As scarpacci mentioned, this somewhat portable if needed.
I know this is bad, but I will post an answer to my own question... Thanks to GBN for the help tho!
I am now storing "flags" in the "description" field of my columns. I, for example, can store a flag this way : "TYPE_2_DATA".
Then, I use this query to get the flag back for each and every column :
select columns.name as [column_name]
,types.name as [type_name]
,extended_properties.value as [column_flags]
from sys.columns
inner join sys.types
on columns.system_type_id = types.system_type_id
left join sys.extended_properties
on extended_properties.major_id = columns.object_id
and extended_properties.minor_id = columns.column_id
and extended_properties.name = 'MS_Description'
where object_id = ( select id from sys.sysobjects where name = 'DimDivision' )
and is_identity = 0
order by column_id
Now I can store metadata about columns without having to create a separate table. I use what's already in place and I don't repeat myself. I'm not sure this is the best possible solution yet, but it works and is far better than duplicating information.
In the future, I will be able to use this field to store more metadata, where as : "TYPE_2_DATA|ANOTHER_FLAG|ETC|OH BOY!".
UPDATE :
I now store the information in separate extended properties. You can manage extended properties using sp_addextendedproperty and sp_updateextendedproperty stored procedures. I have created a simple store procedure that help me to update those values regardless if they currently exist or not :
create procedure [dbo].[UpdateSCDType]
#tablename nvarchar(50),
#fieldname nvarchar(50),
#scdtype char(1),
#dbschema nvarchar(25) = 'dbo'
as
begin
declare #already_exists int;
if ( #scdtype = '1' or #scdtype = '2' )
begin
select #already_exists = count(1)
from sys.columns
inner join sys.extended_properties
on extended_properties.major_id = columns.object_id
and extended_properties.minor_id = columns.column_id
and extended_properties.name = 'ScdType'
where object_id = (select sysobjects.id from sys.sysobjects where sysobjects.name = #tablename)
and columns.name = #fieldname
if ( #already_exists = 0 )
begin
exec sys.sp_addextendedproperty
#name = N'Scd_Type',
#value = #scdtype,
#level0type = N'SCHEMA',
#level0name = #dbschema,
#level1type = N'TABLE',
#level1name = #tablename,
#level2type = N'COLUMN',
#level2name = #fieldname
end
else
begin
exec sys.sp_updateextendedproperty
#name = N'Scd_Type',
#value = #scdtype,
#level0type = N'SCHEMA',
#level0name = #dbschema,
#level1type = N'TABLE',
#level1name = #tablename,
#level2type = N'COLUMN',
#level2name = #fieldname
end
end
end
Thanks again