Firebird - How to use "(? is null)" for selecting blank parameters - sql

I am working with an Excel Report linked to a Firebird 2.0 DB and I have various parameters linked to cell references that correspond to drop down lists.
If a parameter is left blank, I want to select all the possible options. I am trying to accomplish this by putting ... WHERE... (? is null), as described in http://www.firebirdsql.org/refdocs/langrefupd25-sqlnull.html , but I get an "Invalid Data Type" error.
I found some Firebird documentation (http://www.firebirdfaq.org/faq92/) where it talks about this error, but it states that "The solution is to cast the value to appropriate datatype, so that all queries return the same datatype for each column." and I'm not quite sure what that means in my situation.
SELECT C.COSTS_ID,
C.AREA_ID,
S.SUB_NUMBER,
S.SUB_NAME,
TP.PHASE_CODE,
TP.PHASE_DESC,
TI.ITEM_NUMBER,
TI.ITEM_DESC,
TI.ORDER_UNIT,
C.UNIT_COST,
TI.TLPE_ITEMS_ID
FROM TLPE_ITEMS TI
INNER JOIN TLPE_PHASES TP ON TI.TLPE_PHASES_ID = TP.TLPE_PHASES_ID
LEFT OUTER JOIN COSTS C ON C.TLPE_ITEMS_ID = TI.TLPE_ITEMS_ID
LEFT OUTER JOIN AREA A ON C.AREA_ID = A.AREA_ID
LEFT OUTER JOIN SUPPLIER S ON C.SUB_NUMBER = S.SUB_NUMBER
WHERE ((C.AREA_ID = 1 OR C.AREA_ID = ?) OR **(? IS NULL))**
AND ((S.SUB_NUMBER = ?) OR **(? IS NULL))**
AND ((TI.ITEM_NUMBER = ?) OR **(? IS NULL))**
AND ((TP.PHASE_CODE STARTING WITH ?) OR **(? IS NULL))**
ORDER BY TP.PHASE_CODE
Any help is greatly appreciated.

If you are not using Firebird 2.5 (but version 2.0 or higher), or if you are using a driver that doesn't support the SQL_NULL datatype introduced in Firebird 2.5, then you need to use an explicit CAST, eg;
SELECT *
FROM TLPE_ITEMS TI
WHERE TI.ITEM_NUMBER = ? OR CAST(? AS INTEGER) IS NULL
This will identify the second parameter as an INTEGER to the driver (and to Firebird), allowing you to set it to NULL.
Now the faq you reference mentions cast the value to appropriate datatype, what they mean is that you should not cast to a data type that might result to conversion errors if it isn't null.
In my example I cast to INTEGER, but if the values are actually strings and you use say "IX0109302" as a value, you will get a conversion error as it isn't an appropriate INTEGER. To prevent that, you would need to cast to a (VAR)CHAR of sufficient length (otherwise you get a truncation error).
If you are using Firebird 1.5 or earlier this trick will not work, see CORE-778, in that case you might get away with something like TI.ITEM_NUMBER = ? OR 'T' = ?, where you set the second parameter to either 'T' (true) or 'F' (false) to signal whether you want everything or not; this means that you need to move the NULL detection to your calling code.

Related

How to write an Open SQL statement with substring in the JOIN ON condition? [duplicate]

I have the following select statement in ABAP:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
INTO corresponding fields of table GT_INSTMUNIC_F
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN EVER AS EV on
MUNIC~POD = EV~VREFER(9).
"where EV~BSTATUS = '14' or EV~BSTATUS = '32'.
My problem with the above statement is that does not recognize the substring/offset operation on the 'ON' clause. If i remove the '(9) then
it recognizes the field, otherwise it gives error:
Field ev~refer is unknown. It is neither in one of the specified tables
nor defined by a "DATA" statement. I have also tried doing something similar in the 'Where' clause, receiving a similar error:
LOOP AT gt_instmunic.
clear wa_gt_instmunic_f.
wa_gt_instmunic_f-mandt = gt_instmunic-mandt.
wa_gt_instmunic_f-bis = gt_instmunic-bis.
wa_gt_instmunic_f-ab = gt_instmunic-ab.
wa_gt_instmunic_f-zzelecdate = gt_instmunic-zzelecdate.
wa_gt_instmunic_f-ZZCERTDATE = gt_instmunic-ZZCERTDATE.
wa_gt_instmunic_f-CONSYEAR = gt_instmunic-CONSYEAR.
wa_gt_instmunic_f-ZDIMO = gt_instmunic-ZDIMO.
wa_gt_instmunic_f-ZZONE_M = gt_instmunic-ZZONE_M.
wa_gt_instmunic_f-ZZONE_T = gt_instmunic-ZZONE_T.
wa_gt_instmunic_f-USAGE_M = gt_instmunic-USAGE_M.
wa_gt_instmunic_f-USAGE_T = gt_instmunic-USAGE_T.
temp_pod = gt_instmunic-pod.
SELECT vrefer
FROM ever
INTO wa_gt_instmunic_f-vrefer
WHERE ( vrefer(9) LIKE temp_pod ). " PROBLEM WITH SUBSTRING
"AND ( BSTATUS = '14' OR BSTATUS = '32' ).
ENDSELECT.
WRITE: / sy-dbcnt.
WRITE: / 'wa is: ', wa_gt_instmunic_f.
WRITE: / 'wa-ever is: ', wa_gt_instmunic_f-vrefer.
APPEND wa_gt_instmunic_f TO gt_instmunic_f.
WRITE: / wa_gt_instmunic_f-vrefer.
ENDLOOP.
itab_size = lines( gt_instmunic_f ).
WRITE: / 'Internal table populated with', itab_size, ' lines'.
The basic task i want to implement is to modify a specific field on one table,
pulling values from another. They have a common field ( pod = vrefer(9) ). Thanks in advance for your time.
If you are on a late enough NetWeaver version, it works on 7.51, you can use the OpenSQL function LEFT or SUBSTRING. Your query would look something like:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN ever AS ev
ON MUNIC~POD EQ LEFT( EV~VREFER, 9 )
INTO corresponding fields of table GT_INSTMUNIC_F.
Note that the INTO clause needs to move to the end of the command as well.
field(9) is a subset operation that is processed by the ABAP environment and can not be translated into a database-level SQL statement (at least not at the moment, but I'd be surprised if it ever will be). Your best bet is either to select the datasets separately and merge them manually (if both are approximately equally large) or pre-select one and use a FAE/IN clause.
They have a common field ( pod = vrefer(9) )
This is a wrong assumption, because they both are not fields, but a field an other thing.
If you really need to do that task through SQL, I'll suggest you to check native SQL sentences like SUBSTRING and check if you can manage to use them within an EXEC_SQL or (better) the CL_SQL* classes.

Set a Variable in a case expression

I am looking to pass declared variables to build my string. I think I want to set my variable via a case expression but I have not done this before. Here is what I have done thus far.
DECLARE #stu_conv AS VARCHAR(5)
-- I think I need a select here.
set #stu_conv = CASE WHEN ITMMASTER.STU_0 ='KG' THEN'2.2'END
SELECT
YPRIMAT.YCROPYR_0
,ITMMASTER.TCLCOD_0
,SPRICLIST.DCGVAL_3
,ITMMASTER.TSICOD_2
,ITMMASTER.ACCCOD_0
,(BASPRI_0*#stu_conv) AS ImportstringAS Importstring
FROM LIVE.YPRIMAT
INNER JOIN LIVE.ITMMASTER ON YPRIMAT.ITMREF_0 = ITMMASTER.ITMREF_0
LEFT OUTER JOIN LIVE.SPRICLIST ON ITMMASTER.TCLCOD_0 = SPRICLIST.PLICRI1_0
WHERE SPRICLIST.PLICRD_0 = 'SPL000020'
I don't see the point for using a variable here, and trying to set it outside the query does not make sense, since you most likely want the value to reset for each row.
I would suggest moving the case expression into the query, as follows:
select
y.ycropyr_0,
i.tclcod_0,
s.dcgval_3,
i.tsicod_2,
i.acccod_0,
baspri_0 * case when i.stu_0 = 'KG' then 2.2 else 1 end as importstringas importstring
from live.yprimat y
inner join live.itmmaster i on y.itmref_0 = i.itmref_0
left outer join live.spriclist s on i.tclcod_0 = s.plicri1_0
where s.plicrd_0 = 'SPL000020'
I assumed that you want a value of 1 when stu_0 is not 'KG', but you can change this as needed.
Side note:
I modified your query to use table aliases. This makes the query shorter to write and somehow easier to read
you would need to prefix column baspri_0 with the table it belongs to (as your query is, it is not possible to tell)
I'm not sure why you're declaring a string and then multiplying it, but I would just inline the case (and add a default case?):
,(BASPRI_0 * CASE
WHEN ITMMASTER.STU_0 ='KG'
THEN 2.2
ELSE ???
END) AS Importstring

How to Join (equal) two data columns that belongs to the String and Double types respectively in Alibaba MaxCompute?

I am not authorized to share the table details.
For instance, let me consider an Example:
I am trying to join two columns of String and Double data types respectively in Alibaba MaxCompute.
In the earlier version of MaxCompute, the String and Double data types are converted to the bigint data type at the cost of precision. 1.1 = “1” in a Join condition.
Whereas the same code does not work in the new version of the MaxCompute. The code syntax is like follows:
SELECT * FROM t1 JOIN t2 ON t1.double_value = t2.string_value;
Error:
WARNING:[1,48] implicit conversion from STRING to DOUBLE, potential data loss, use CAST function to suppress
What is the correct syntax to do the join operation in Alibaba MaxCompute V2?
I did a bit of digging and it seems like this SQL command is the recommended way of getting around this issue.
select * from t1 join t2 on t.double_value = cast(t2.string_value as double);
As the error message suggests:
SELECT *
FROM t1 JOIN
t2
ON CAST(t1.double_value as string) = t2.string_value;

Error converting data type varchar to bigint. when using update

I'm using SQL Server. I'm trying to run the following SQL script and I'm getting this error:
Error converting data type varchar to bigint.
This is the script:
with T as
(
select
sp.ProfileId
,sp.ProfileHandle
,sp.[Description]
from
[SocialProfile] sp
inner join
Entity e on sp.EntityId = e.EntityId
where
e.EntityStatusId != 3 and e.EntityStatusId != 4
and sp.SocialProfileTypeId in (1, 2, 10)
and (ISNUMERIC(sp.ProfileHandle) = 1)
and IsRemoved = 0
)
update T
set ProfileHandle = NULL
where ProfileHandle = ProfileId
I tried to use cast function but it doesn't work. Can someone tell me what I'm doing wrong?
The isnumeric() doesn't give the protection you want. Use try_convert() for the comparison:
with T as (
select sp.ProfileId, sp.ProfileHandle, sp.[Description]
from [SocialProfile] sp inner join
Entity e
on sp.EntityId = e.EntityId
where e.EntityStatusId not in (3, 4) and
sp.SocialProfileTypeId in (1, 2, 10) and
ISNUMERIC(sp.ProfileHandle) = 1 and -- you can leave it in
IsRemoved = 0
)
update T
set ProfileHandle = NULL
where try_convert(int, ProfileHandle) = ProfileId;
SQL Server has a "feature" where it will rearrange the conditions in the query. The CTE does not get executed "first", so the isnumeric() is not necessarily run before the conversion in the where. I consider this a bug. Microsoft considers this a feature (because it provides more options for optimization).
The only ordering guaranteed in a query is through case expressions. The simplest work-around is try_convert().
In addition, I strongly recommend never relying on implicit conversions. Always explicitly convert. I have spent many hours debugging code for problems caused by implicit conversion.

How can I produce a join on a substring and an integer in Entity Framework?

Using Entity Framework, I'm trying to join two tables like this.
...
join f in ent.FTypes on Int32.Parse(c.CourseID[0].ToString()) equals f.FTypeID
...
The first character of the string CourseID is a digit, and FTypeID is an int.
This doesn't seem to work though. The exception message I get is:
LINQ to Entities does not recognize the method 'Int32 Parse(System.String)' method, and this method cannot be translated into a store expression."} System.Exception {System.NotSupportedException}
What I want to replicate is the SQL string equivalent (which works fine):
join FType f on SUBSTRING(c.CourseID, 1, 1) = f.FTypeID
Does anyone have the solution to have to do this in LINQ to Entities?
This is a rather nasty join, but I did some testing in Entity Framework with similar data and arrived at something you can test on yours.
It uses string.Substring to grab the first character from your string operand, and then uses a combination of the EF-only method SqlFunctions.StringConvert (these methods are found in System.Data.Objects.SqlClient) with a cast to double1 and finally a string.Trim2 for your integer operand.
I have tested this and confirmed that all functions are supported at least in EF 4. Several other methods proposed or featured in the question do not work, because Entity Framework does not know how to tranlsate them to the appropriate SQL equivalent.
join f in ent.FTypes
on c.CourseID.Substring(0, 1)
equals SqlFunctions.StringConvert((double)f.FTypeID).Trim()
It produces a SQL join that looks like the following:
INNER JOIN [dbo].[FTypes] AS [Extent2]
ON ((SUBSTRING([Extent1].[CourseID], 0 + 1, 1)) = (LTRIM(RTRIM(STR( CAST( [Extent2].[FTypeID] AS float))))))
OR ((SUBSTRING([Extent1].[CourseID], 0 + 1, 1) IS NULL) AND (LTRIM(RTRIM(STR( CAST( [Extent2].[FTypeID] AS float)))) IS NULL))
So based on that, you might want to do some additional filtering as necessary.
Give it a shot and see if that helps solve the problem.
1 The cast to double is necessary because SqlFunctions.StringConvert does not have an overload for integer and there is no other single best match, so I force one.
2 The resultant string needs to be trimmed because the string conversion generates some excess padding.
I'm not sure this worked 8 years ago, but modern EF6 implementation allows you to add anonymous types, so you can add the conversion to your initial select statements, and then you can just compare that new properties directly in the join.
from c in ent.Courses
select new { typeId = c.CourseId.Substring(0, 1), c }
join f in ent.FTypes.Select(t => new { stringId =
t.FTypeId.ToString(), t }
on c.typeId equals f.stringId into ...
NOTE: The join-statement does not appear to support ToString() but the select-statements do.
You could also move the CouresId.Substring into the join statement, but that may run less efficiently there.
Results in SQL like this:
INNER JOIN [ent].[FTypes] AS [Extent2] ON
(LTRIM(RTRIM(SUBSTRING([Extent1].[nvarchar(max)], 3 + 1, 1)))) =
CAST([Extent2].[Id] AS nvarchar(max))