why my symmetric difference is not true? - gams-math

i want to calculate symmetric difference between two set V(i,ip) and v(ipp,ippp)
i have set i={1,3,4,6} and j={1,2,..,10}
i have set T(i,j) and , 3 set V,W,b too , that
T(1)={2,5,10}
T (3)={7,10}
T (4)={2,5,6}
T (6)={2,5}
and V is union of T(i) , W is symmetric difference between T and V , and b is a difference between V(i,ip)
the result of my b is not true! why ?
how can i get difference between V(i,ip)?
v(i,ip)=t(i,j)+t(ip,j)
w(i,ip,ipp)= (t(i,j)+v(ip,ipp,j))-(t(i,j)*v(ip,ipp,j));
b(i,ip,ipp,ippp)=((v(i,ip,j)+v(ipp,ippp,j))-(v(ip,ipp,j)*v(ipp,ippp,j))) ;
*---- index and sets----*
sets
i /1,3,4,6/
j/1*10/
t(i,j) /
1.(2,5,10)
3.(7,10)
4.(2,5,6)
6.(2,5)
/ ;
alias(i,ip,ipp,ippp);
*---- parameter----*
parameter
MyOrd(i,ip);
*-------------------------------
MyOrd(i,ip)=i.val+(ip.val -1)*4;
set
v(i,ip,j)'for union only'
b(i,ip,ipp,ippp,j) 'symmetric diffrence between v and v'
w(i,ip,ipp,j) 'symmetric diffrence between t and v'
;
v(i,ip,j)$(i.val<ip.val)=t(i,j)+t(ip,j);
w(i,ip,ipp,j)$(ip.val<ipp.val) =(t(i,j)+v(ip,ipp,j))-(t(i,j)*v(ip,ipp,j));
b(i,ip,ipp,ippp,j)$(i.val<ip.val and ipp.val<ippp.val and MyOrd(i,ip)
display t,v,w,b;

in your code you write
MyOrd(i,ip)=i.val+(ip.val -1)*4;
try with
parameter
MyOrd(i,i1);
scalar
counter /1/
;
loop((i,i1)$(vulnerable(i) and vulnerable(i1) and i.val <i1.val ),
MyOrd(i,i1)=counter;
counter=counter+1;
);

Related

How to calculate superscript values in SQL

I have prices coming in my source file like below -
78-22¼,
78-18⅝
I need to calculate these price. For example for first case result should be 78-22.25. I searched a lot but found that SQL supports few of these characters only. Is there anyway to make sure we are able to calculate for whatever value we are getting. Solution in either SQL or PowerShell could work.
You could write a PowerShell function to convert the fractions to decimals:
PS ~> ConvertTo-Decimal '18⅝'
18.625
To do so, we'll need to write a function that:
Uses regex to identify and extract the integer and fraction parts
Uses [char]::GetNumericValue() to get the decimal representation of the fraction
Outputs the sum of the two
function ConvertTo-Decimal {
param(
[Parameter(Mandatory)]
[string]$InputObject
)
if($InputObject -match '^(-?)(\d+)(\p{No})?$'){
$baseValue = +$Matches[2]
if($Matches[3]){
$baseValue += [char]::GetNumericValue($Matches[3])
}
if($Matches[1] -eq '-'){
$baseValue *= -1
}
return $baseValue
}
return 0
}
Hoo this one was fun.
If you want to do it purley in TSQL give this a tug:
DECLARE #table TABLE (Glyph NVARCHAR(2), Dec DECIMAL(8,6))
INSERT INTO #table (Glyph, Dec) VALUES
(N'¼', 1.0/4),(N'½', 1.0/2),(N'¾', 3.0/4),(N'⅐', 1.0/7),
(N'⅑', 1.0/8),(N'⅒',1.0/10),(N'⅓', 1.0/3),(N'⅔', 2.0/3),
(N'⅕', 1.0/5),(N'⅖', 2.0/5),(N'⅗', 3.0/5),(N'⅘', 4.0/5),
(N'⅙', 1.0/6),(N'⅚', 5.0/6),(N'⅛', 1.0/8),(N'⅜', 3.0/8),
(N'⅝', 5.0/8),(N'⅞', 7.0/8),(N'⅟', 1.0/1)
DECLARE #values TABLE (ID INT IDENTITY, value NVARCHAR(20))
INSERT INTO #values (value) VALUES
(N'78-22¼'),(N'78-18⅝'),(N'10+1')
;WITH sort AS (
SELECT v.*, t.*,
CASE WHEN m.value = v.value THEN
CASE WHEN t.Dec IS NOT NULL THEN REPLACE(p.value,t.Glyph,'')+dec
ELSE p.value
END
ELSE
CASE WHEN t.Dec IS NOT NULL THEN REPLACE(m.value,t.Glyph,'')+dec
ELSE m.value
END
END AS v,
CASE WHEN m.value = v.value THEN '+'
ELSE '-' END AS op,
ROW_NUMBER() OVER (PARTITION BY v.value ORDER BY CASE WHEN m.value = v.value THEN CHARINDEX(m.value,v.value) ELSE CHARINDEX(p.value,v.value) END) AS subID
FROM #values v
OUTER APPLY STRING_SPLIT(v.value,'-') m
OUTER APPLY STRING_SPLIT(v.value,'+') p
LEFT OUTER JOIN #table t
ON RIGHT(CASE WHEN m.value = v.value THEN p.value ELSE m.value END,1) = t.Glyph
)
SELECT ID, value, SUM(v * CASE WHEN subId = 1 THEN 1 WHEN op = '+' THEN 1 ELSE -1 END) AS v
FROM sort
GROUP BY ID, value
ID value v
---------------------
1 78-22¼ 55.750000
2 78-18⅝ 59.375000
3 10+1 11.000000
#values replaces your table.
disclaimer: this works, it'll probably perform like hot garbage, but it works :P
In T-SQL you could write a function like this that takes a vulgar fraction and replaces it with its decimal equivalent (this is not completely exhaustive, but handles the most common fractions, and makes a guess about whether you want .666 or .667 or something else for ⅔):
CREATE FUNCTION dbo.FractionToDecimal(#str nvarchar(255))
RETURNS TABLE
AS
RETURN
(
SELECT str = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
REPLACE(REPLACE(REPLACE(REPLACE(#str, N'¼','.25'),
N'½','.5'), N'¾','.75'), N'⅓','.333'), N'⅔','.666'),
N'⅛','.125'), N'⅜','.375'), N'⅝','.625'), N'⅞','.875')
);
Sample usage:
DECLARE #x table(str nvarchar(255));
INSERT #x VALUES(N'78-22¼'),(N'78-18⅝');
SELECT [input] = i.str, [output] = o.str
FROM #x AS i
CROSS APPLY dbo.FractionToDecimal(str) AS o;
Output:
input
output
78-22¼
78-22.25
78-18⅝
78-18.625
Working example in this fiddle.
Note there are only so many fraction codes available that you could be pulling in ... so you could add any to the above e.g. from this set but it isn't the case that you would have to handle any possible fraction, only the ones that are represented by these specific symbols. If someone passes in plain text 9/10 or 31/33 that is a completely different problem space than what you have outlined in the question.

HANA database - Injection of a Table User Defined Function into a lateral join

In short
On an HANA database, I have set a Table User Defined Function which returns a 1-row table with 3 columns ;
I would like to use it inside a lateral join but so far my attempts have been to no avail.
The problem
Let's say we have the following dummy Table User Defined Function :
CREATE OR REPLACE FUNCTION PBANALYST. F__ITEM_MBEW(
IN
p_str_MATNR NVARCHAR(18)
, p_str_BWKEY NVARCHAR(02)
, p_str_VALDATE NVARCHAR(08)
)
RETURNS
TABLE(
VALDATE NVARCHAR(08)
, LBKUM INTEGER
, VERPR DECIMAL
, STPRS DECIMAL
)
LANGUAGE SQLSCRIPT
SQL SECURITY INVOKER
AS
BEGIN
RETURN
SELECT
'20220928' AS VALDATE
, 10 AS LBKUM
, 5.3 AS VERPR
, 10.5 AS STPRS
FROM DUMMY
;
END;
It works fine on its own.
But when I try to inject it inside a lateral join, I get an error :
DO
BEGIN
tbl_MATNR_LIST =
SELECT '000000000000824151' AS MATNR , '92' AS div , '20220715' AS VALDATE FROM dummy
;
SELECT
tbl_MATNR_LIST. *
FROM :tbl_MATNR_LIST tbl_MATNR_LIST ,
LATERAL(
SELECT *
FROM F__ITEM_MBEW(
'000000000000824151'
, '92'
, '20220715'
)
) MBEW
;
END;
DataSource.Error : ODBC: ERROR [S1000] [SAP AG][LIBODBCHDB DLL][HDBODBC] General error;318 decimal precision specifier is out of range: -1: (1 to 38)
How can I fix it?
Thank you for your help.
The error message
DataSource.Error : ODBC: ERROR [S1000] [SAP AG][LIBODBCHDB DLL][HDBODBC] General error;318 decimal precision specifier is out of range: -1: (1 to 38)
can easily be fixed by specifying the decimal data type lengths in the user defined function:
TABLE(
VALDATE NVARCHAR(08)
, LBKUM INTEGER
, VERPR DECIMAL (10, 2)
, STPRS DECIMAL (10, 2)
)
Unfortunately, that does not help with the overall goal: to use the UDF in the LATERAL join in a meaningful way.
What does work is something like this:
DO
BEGIN
tbl_MATNR_LIST =
SELECT '000000000000824151' AS MATNR
, '92' AS div
, '20220715' AS VALDATE
FROM dummy;
SELECT
tml. *
FROM :tbl_MATNR_LIST tml
CROSS JOIN LATERAL(
F__ITEM_MBEW( '000000000000824151'
, '92'
, '20220715')
) MBEW;
END;
This does return the result, but obviously does not use any "lateral" references.
If one tries to make "grab stuff side-ways" this is the result:
[...]
CROSS JOIN LATERAL(
F__ITEM_MBEW( '000000000000824151'
, tml.div
, '20220715')
) MBEW;
[...]
SQL Error [7] [HY000]: SAP DBTech JDBC: [7] (at 292): feature not supported: non-field expression with LATERAL: line 13 col 29 (at pos 292)
At this point, I would guess that this way of using the LATERAL JOIN is just not supported (on version 2.00.057).

Could someone help me understand the ambiguity here in Postgres?

So I've been trying out PG for a few days, specifically through NpgSQL in dotnet core, but I don't believe that is relevant to my question. I've been writing a couple of update functions. The first one was easy:
CREATE OR REPLACE FUNCTION "Api"."UpdateExpenseReceipt" ( "vReceiptID" UUID , "vTotal" DOUBLE PRECISION , "vTaxPercent" DOUBLE PRECISION , "vShippingCost" DOUBLE PRECISION , "vReceiptDate" TIMESTAMP , "vReference" VARCHAR , "vCurrentToken" UUID )
RETURNS TABLE ( ReceiptID UUID , Total DOUBLE PRECISION , TaxPercent DOUBLE PRECISION , ShippingCost DOUBLE PRECISION , Reference VARCHAR(96) , ReceiptDate TIMESTAMP )
LANGUAGE PLPGSQL
AS $$
DECLARE "iValidReceipt" INTEGER;
DECLARE "iValidUser" INTEGER;
BEGIN
"iValidReceipt" := ( SELECT COUNT("ReceiptID") FROM "Users"."ExpenseReceipt" WHERE "ReceiptID" = "vReceiptID" );
"iValidUser" := ( SELECT COUNT("AccountID") FROM "Users"."Account" WHERE "CurrentToken" = "vCurrentToken" LIMIT 1 );
IF "iValidUser" = 0 THEN
RAISE 'Error' USING ERRCODE = '10001';
END IF;
IF "iValidReceipt" > 0 THEN
UPDATE "Users"."ExpenseReceipt" SET
"Total" = COALESCE( "vTotal" , "Total" )
, "TaxPercent" = COALESCE( "vTaxPercent" , "TaxPercent" )
, "ShippingCost" = COALESCE( "vShippingCost" , "ShippingCost" )
, "Reference" = COALESCE( CAST( "vReference" AS VARCHAR ) , "Reference" )
, "ReceiptDate" = COALESCE( "vReceiptDate" , "ReceiptDate" )
, "EditDate" = current_timestamp at time zone 'utc'
WHERE "ReceiptID" = "vReceiptID";
RETURN QUERY
SELECT
"ReceiptID"
, "Total"
, "TaxPercent"
, "ShippingCost"
, "Reference"
, "ReceiptDate"
FROM "Users"."ExpenseReceipt"
WHERE "ReceiptID" = "vReceiptID";
ELSE
RAISE 'Error' USING ERRCODE = '10101';
END IF;
END; $$
--I'll include the table itself in case its relevant
CREATE TABLE "Users"."ExpenseReceipt"
(
"ReceiptID" UUID NOT NULL DEFAULT uuid_generate_v4(),
"AccountID" UUID NOT NULL ,
"Total" DOUBLE PRECISION NOT NULL ,
"TaxPercent" DOUBLE PRECISION DEFAULT 0.0 ,
"ShippingCost" DOUBLE PRECISION DEFAULT 0.0 ,
"Reference" VARCHAR(96) ,
"ReceiptDate" TIMESTAMP ,
"EditDate" TIMESTAMP DEFAULT ( current_timestamp at time zone 'utc' )
);
Easy update function, uses coalesce to not update the value if the API call doesn't set them. Everything works fine (after dealing through the numerous naming issues I've run into with postgres, and there I think NpgSQL is relevant). I know how to get it right. I made a second one, basically exactly the same:
CREATE OR REPLACE FUNCTION "Api"."UpdateSupplyItem" ( "vItemID" UUID , "vDescription" VARCHAR , "vSize" VARCHAR , "vNetCost" DOUBLE PRECISION , "vPackageQuantity" DOUBLE PRECISION , "vNetWeight" DOUBLE PRECISION , "vCurrentToken" UUID )
RETURNS TABLE ( "ItemID" UUID , "Description" VARCHAR , "Size" VARCHAR , "NetCost" DOUBLE PRECISION , "PackageQuantity" DOUBLE PRECISION , "NetWeight" DOUBLE PRECISION )
LANGUAGE PLPGSQL
AS $$
DECLARE "iValidItem" INTEGER;
DECLARE "iValidUser" INTEGER;
BEGIN
"iValidItem" := ( SELECT COUNT(usi."ItemID") FROM "Users"."SupplyItem" usi WHERE usi."ItemID" = "vItemID" );
"iValidUser" := ( SELECT COUNT("AccountID") FROM "Users"."Account" WHERE "CurrentToken" = "vCurrentToken" LIMIT 1 );
IF "iValidUser" = 0 THEN
RAISE 'Error' USING ERRCODE = '10001';
END IF;
IF "iValidItem" > 0 THEN
UPDATE "Users"."SupplyItem"
SET
"Description" = COALESCE( "vDescription" , "Users"."SupplyItem"."Description" )
, "Size" = COALESCE( "vSize" , "Users"."SupplyItem"."Size" )
, "NetCost" = COALESCE( "vNetCost" , "Users"."SupplyItem"."NetCost" )
, "PackageQuantity" = COALESCE( "vPackageQuantity" , "Users"."SupplyItem"."PackageQuantity")
, "NetWeight" = COALESCE( "vNetWeight" , "Users"."SupplyItem"."NetWeight" )
WHERE "Users"."SupplyItem"."ItemID" = "vItemID";
RETURN QUERY SELECT usi."ItemID" , usi."Description" , usi."Size" , usi."NetCost" , usi."PackageQuantity" , usi."NetWeight" FROM "Users"."SupplyItem" usi WHERE usi."ItemID" = "vItemID" LIMIT 1;
ELSE
RAISE 'Error' USING ERRCODE = '10401';
END IF;
END; $$
--Again, the table in case it helps
CREATE TABLE "Users"."SupplyItem"
(
"ItemID" UUID NOT NULL DEFAULT uuid_generate_v4() ,
"AccountID" UUID NOT NULL ,
"Description" VARCHAR ,
"Size" VARCHAR ,
"NetCost" DOUBLE PRECISION ,
"PackageQuantity" DOUBLE PRECISION ,
"NetWeight" DOUBLE PRECISION
);
but it's quite different. You can see clearly that I've had to fully qualify the right hand side of every equals (where the earlier function had no need). I get ambiguity all the way down the update statement. It starts at ItemID, then Description, then Size... every attribute I think. First thing I did was alias the table
UPDATE usi [...] FROM "Users"."SupplyItem" usi
but that failed because you can't do short aliasing in an UPDATE in PG (relation usi."[...]" does not exist") which actually kind of sucks. I only figured out that it needed to be fully qualified when someone asked a similar question and the answer was "It must be a quirk of RETURNS TABLE."
So why is my second update "a quirk" but my first update works perfectly? I've had a tough time with PG (and I'm not a slouch), but having two functions that seem identical having entirely different results (at runtime no less) makes me uncomfortable. I'm posting here because I know the two functions must be markedly different; 99.9% of the time, there is no such thing as a "quirk." There is something I need to understand to work around to avoid in the future. What is the "gotcha" that I've missed in the second UPDATE function?
The problem is that you have a function variable "Size" (an OUT parameter defined in the RETURNS TABLE clause) and a column "Size" in "Users"."SupplyItem". So you have to qualify the reference to indicate what you mean.
I recommend using an alias for simplicity:
UPDATE "Users"."SupplyItem" AS si
SET "Size" = COALESCE("vSize" , si."Size")
...
There is no such ambiguity in your first example, because you didn't double quote the parameter TaxPercent, so it gets case folded to taxpercent and is different from the column "TaxPercent".

In Firebird using SIMILAR TO how to find if several words match

I'm using Firebird 2.5 and I have to find rows containing several word in any order:
find 'blue' and 'house':
'a blue house in the woods' = true
'a house with blue windows' = true
'a house by the beach' = false
'the blue car' = true
Using a pipe "|" give me OR and I need AND but in any order, not only with 2 words, could be more
I'm trying using SIMILAR TO but seems that RegExpr in Firebird is too limited.
Using several LIKE x AND LIKE y, is not the way yo go because I don't know how many words will have to find.
You can solve your task using Firebird means only. Assume, you have a table named TEST with sole field S.
CREATE TABLE TEST (S VARCHAR(256))
Which contains phrases:
'a blue house in the woods'
'a house with blue windows'
'a house by the beach'
'the blue car'
...
You will need create an auxiliary selectable procedure SPLIT_WORDS:
CREATE OR ALTER PROCEDURE split_words (S VARCHAR(256))
RETURNS(
K VARCHAR(256),
W VARCHAR(256))
AS
DECLARE VARIABLE B INTEGER = 1;
DECLARE VARIABLE E INTEGER = 1;
DECLARE VARIABLE C CHAR(1);
BEGIN
K = :S;
WHILE (:E <= CHAR_LENGTH(:S)) DO
BEGIN
C = UPPER(SUBSTRING(:S FROM :E FOR 1));
IF (:C < 'A' OR :C > 'Z') THEN
BEGIN
W = SUBSTRING(:S FROM :B FOR (:E - :B));
IF (:W > '') THEN
SUSPEND;
WHILE (:E <= CHAR_LENGTH(:S)) DO
BEGIN
C = UPPER(SUBSTRING(:S FROM :E FOR 1));
IF (:C >= 'A' AND :C <= 'Z') THEN
LEAVE;
E = :E + 1;
END
B = :E;
END
E = :E + 1;
END
W = SUBSTRING(:S FROM :B FOR (:E - :B));
IF (:W > '') THEN
SUSPEND;
END
The procedure designed to split given string into words. Any non letters chars are treated as separators.
Having this procedure one can write a query wich will returns all strings with both words 'HOUSE' and 'BLUE' in any order.
SELECT
tt.s
FROM
test tt JOIN
(SELECT
t.s, COUNT(s2.w) c
FROM
test t LEFT JOIN split_words(t.s) s ON 1=1
LEFT JOIN split_words('blue house') s2 ON s.w=s2.w
WHERE
s2.w IS NOT NULL
GROUP BY
1
) ttt ON ttt.s = tt.s
WHERE
ttt.c = (SELECT COUNT(*) FROM split_words('blue house'))
To my knowledge, there isn't anything built-in to firebird that is going to help you.
What you really need is a full text search. Although this is not directly supported by firebird, there are some useful suggestion here: http://www.firebirdfaq.org/faq328/
Best of luck and sorry I don't have a more direct answer.
You can check SIMILAR TO in reference manual but I think what you need is more something like Sphinx

BRACKETS in SQL if conditions

I have a Rate column in one of my SQL Table where Data is stored.
The data in the rate column , is coming thro an Stored proc.
In that existing stored proc, I found the condition
BEGIN
IF #taxPrd < '07/01/2000' OR
#taxPrd >= '11/01/2001' AND #taxPrd < '04/01/2003'
BEGIN
I changed it to ...so as to make it true for ALL periods after sep-1/09
BEGIN
IF #taxPrd < '07/01/2000' OR
( #taxPrd >= '11/01/2001' AND #taxPrd < '04/01/2003' ) OR
#taxPrd >= '09/01/2009'
BEGIN
hence now the rate is valid for
all less than Jul2000 OR
all in between Nov2001 and Apr2003 OR
all more than Sep2009
is the placements of brackets correct?
In your case the brackets are not necessary, (although I'd leave them there for clarity) because the boolean Operator AND takes precedence over the OR operator. i.e., whenever there is a mixture of ORs and ANDs in a predicate, the ANDs are processed first, and then the resulting expression (with ORs in it ) is processed.
for example
A Or B Or C And D Or E And F And G Or H
would be processed as
A Or B Or (C And D) Or (E And F And G) Or H
\ / \ /
|| ||
A Or B Or C&D Or E&F&G Or H
Your parentheses are placed correctly for what you want to achieve.