Why does Visual FoxPro not give the expected answer for certain comparisons? - comparison

The following behaviour from Visual FoxPro puzzles me.
CREATE TABLE test_17 (A C(2), B N(10,2))
CREATE TABLE test_18 (A C(2), B N(20))
INSERT INTO test_17 values ('C1', 1037.60)
INSERT INTO test_17 values ('C2', 2411.50)
INSERT INTO test_18 VALUES ('C1', 1037600)
INSERT INTO test_18 VALUES ('C2', 2411500)
The following query
SELECT * FROM test_17 a, test_18 b WHERE a.A = b.A AND a.B*1000 = b.B
only returns the C2 line and not the C1 line whereas:
SELECT * FROM test_17 a, test_18 b WHERE a.A = b.A AND a.B*1000 <> b.B
returns nothing as expected and
SELECT IIF(a.B*1000 = b.B,'T','F') FROM test_17 a, test_18 b WHERE a.A = b.A
returns T, 'T' as expected.
Can someone please explain to me why Visual FoxPro behaves this way? Thank you.

Try
int(a.B*1000) = b.B
In all digital devices, floating point numbers are an approximation, comparisons of them are just a guess.

Related

Snowflake query with column alias in join failing

This query works in one account but throws an error (below) in another. Is there a parameter that affects this behaviour? Both accounts have the same Snowflake version (6.41.2).
Error Message
with cte_one as (
select 'aaa' a
,'bbb' b
)
select t1.a || t1.b as c
from cte_one t1
join cte_one t2 on c = t2.a || t2.b
;
This will work.
with cte_one as (
select 'aaa' a
,'bbb' b
), t3 as
(
select t1.a || t1.b as c
from cte_one t1
)
select * from t3
join cte_one t2 on c = t2.a || t2.b
;
C
A
B
aaabbb
aaa
bbb
Virtual column C doesn't exist yet in the original syntax when the compiler goes into the join. As humans we can read what the original SQL is trying to do and it makes sense. The compiler sees it differently.
Out of curiosity I went to SQL Fiddle I tried the original SQL on Oracle, Postgres, and MS SQL Server. They all reported errors saying C does not exist.
The provided code sample works as-is:
Environment:
SELECT CURRENT_REGION(), CURRENT_VERSION();
-- AZURE_WESTEUROPE 6.41.2
Query profile:
explain using tabular
with cte_one as (
select 'aaa' a ,'bbb' b union all select 'x', 'y'
)
select t1.a || t1.b as c, 1
from cte_one t1
join cte_one t2
on c = t2.a || t2.b;
Output:
There is a feature for resolving aliases from the select list in the join predicate. The rollout process was not completed yet for all accounts. You should engage Snowflake Support and ask them to fix the configuration discrepancies on your accounts.
I would suggest disabling it on both accounts and using Greg's workaround.

Intercalate multiple columns when some of those columns must remain in the same row

In Column A I have the id of the home team, B the name of the home team, C the id of the visiting team and in D the name of the visiting team:
12345 Borac Banja Luka 98765 B36
678910 Panevezys 43214 Milsami
1112131415 Flora 7852564 SJK
1617181920 Magpies 874236551 Dila
I want to create a column of ids and another of names but keeping the sequence of who will play with whom:
12345 Borac Banja Luka
98765 B36
678910 Panevezys
43214 Milsami
1112131415 Flora
7852564 SJK
1617181920 Magpies
874236551 Dila
Currently (the model works) I'm joining the columns with a special character, using flatten and finally split:
=ARRAYFORMULA(SPLIT(FLATTEN({
FILTER(A1:A&"§§§§§"&B1:B,(A1:A<>"")*(B1:B<>"")),
FILTER(C1:C&"§§§§§"&D1:D,(C1:C<>"")*(D1:D<>""))
}),"§§§§§"))
Is there a less archaic and correct approach to working in this type of case?
Spreadsheet to tests
889
A
5687
C
532
B
8723
D
Stack up the columns using {} and SORT them by a SEQUENCE of 1,2,1,2:
=SORT({A1:B2;C1:D2},{SEQUENCE(ROWS(A1:B2));SEQUENCE(ROWS(A1:B2))},1)
889
A
5687
C
532
B
8723
D
You can also try with function QUERY, enter this formula in F1:
={QUERY((A1:B), "SELECT * WHERE A IS NOT NULL and B IS NOT NULL",1);
QUERY((C1:D), "SELECT * WHERE C IS NOT NULL and D IS NOT NULL",1)}

Select if multiple variables missing

How would I find the id of a person in my table who has SYSMIS for all the variables A,B,C & D? A to D are sequential variables.
I can't do a select if because it doesn't accept the to in:
select if (A to D) = SYSMIS.
This will do it:
select if nmiss(A to D)=4.
If the variables weren't sequential you could use
select if nmiss(A, B, C, D)=4.

Run 2 select statement in DB2 depending of the result of one of them

I'm trying to do an SP in DB2 with 2 select statements. If the first select returns null, perform the second one.
For example
Select a, b, c from table A where...
--If first select returns null
Select a, from table B where...
I tried a lot of ideas but none of them worked.
Thanks
You can use this general pattern, of course you will have to adapt your two result sets to match
WITH first AS
(
SELECT ..result1.. FROM table1
WHERE ..clause1..
)
SELECT ..result1.. FROM first
UNION
SELECT ..result2.. FROM table2
WHERE 0=(SELECT COUNT(1) FROM first)
AND
..clause2..
Here is a simple way to write that
Select a, from table B where...
and not exists (select * from table a where...)
union
select a,.. from table A)

Can a DB2 WITH statement be used as part of an UPDATE or MERGE?

I need to update some rows in a DB table. How I identify the rows to be updated involved a series of complicated statements, and I managed to boil them down to a series of WITH statements. Now I have the correct data values, I need to update the table.
Since I managed to get these values with a WITH statement, I was hoping to use it in the UPDATE/MERGE. A simplified example follows:
with data1
(
ID_1
)
as
(
Select ID
from ID_TABLE
where ID > 10
)
,
cmedb.data2
(
MIN_ORIGINAL_ID
,OTHER_ID
)
as
(
Select min(ORIGINAL_ID)
,OTHER_ID
from OTHER_ID_TABLE
where OTHER_ID in
(
Select distinct ID_1
From data1
)
group by OTHER_ID
)
select MIN_ORIGINAL_ID
,OTHER_ID
from cmedb.data2
Now I have the two columns of data, I want to use them to update a table. So instead of having the select at the bottom, I've tried all sorts of combinations of merges and updates, including having the WITH statement above the UPDATE/MERGE, or as part of the UPDATE/MERGE statement. The following is what comes closest in my mind to what I want to do:
merge into ID_TABLE as it
using
(
select MIN_ORIGINAL_ID
,OTHER_ID
from cmedb.data2
) AS SEL
ON
(
it.ID = sel.OTHER_ID
)
when matched then
update
set it.ORIGINAL_ID = sel.MIN_ORIGINAL_ID
So it doesn't work. I'm unsure if this is even possible, as I've found no examples on the internet using WITH statements in combination with UPDATE or MERGE. I have examples of WITH statements being used in conjunction with INSERT, so believe it might be possible.
If anyone can help it would be great, and please let me know if I've left out any information that would be useful to solve the problem.
Disclaimer: The example I've provided is a boiled down version of what I'm trying to do, and may not actually make any sense!
As #Andrew White says, you can't use a common table expression in a MERGE statement.
However, you can eliminate the common table expressions with nested subselects. Here is your example select statement, rewritten using nested subselects:
select min_original_id, other_id
from (
select min(original_id), other_id
from other_id_table
where other_id in (
select distinct id_1 from (select id from id_table where id > 10) AS DATA1 (ID_1)
)
group by other_id
) AS T (MIN_ORIGINAL_ID, OTHER_ID);
This is somewhat convoluted (the exact statement could be written better), but I realize that you were just giving a simplified example.
You may be able to rewrite your MERGE statement using nested subselects instead of common table expressions. It is certainly syntactically possible.
For example:
merge into other_id_table x
using (
select min_original_id, other_id
from (
select min(original_id), other_id
from other_id_table
where other_id in (
select distinct id_1 from (select id from id_table where id > 10) AS DATA1 (ID_1)
)
group by other_id
) AS T (MIN_ORIGINAL_ID, OTHER_ID)
) as y
on y.other_id = x.other_id
when matched
then update set other_id = y.min_original_id;
Again, this is convoluted, but it shows you that it is at least possible.
A way to use WITH statement with UPDATE (and INSERT too) is using SELECT FROM UPDATE statement (here):
WITH TEMP_TABLE AS (
SELECT [...]
)
SELECT * FROM FINAL TABLE (
UPDATE TABLE_A SET (COL1, COL2) = (SELECT [...] FROM TEMP_TABLE)
WHERE [...]
);
I'm looking up the grammar now but I am pretty sure the answer is no. At least not in the version of DB2 I last used. Take a peek at the update and merge doc pages for their syntax. Even if you see the fullselect in the syntax you can't use with as that is explicitly separate according to the select doc page.
If you're running DB2 V8 or later, there's an interesting SQL hack here that allows you to UPDATE/INSERT in a query with a WITH statement. For inserts & updates that require a lot of preliminary data prepping, I find this method offers a lot of clarity.
Edit One correction here - selecting from UPDATE statements was introduced in V9 i believe, so the above will work for inserts on V8 or greater, and updates for V9 or greater.
Put the CTEs into a view, and select from the view in the merge. You get a clean, readable view that way, and a clean, readable merge.
Another method is to simply substitute your WITH queries and just use subselects.
For example, if you had (and I tried to include a somewhat complex example with some WHERE logic, an aggregate function (MAX) and a GROUP BY, just to show it more real world):
WITH
Q1 AS (
SELECT
A.X,
A.Y,
A.Z,
MAX(A.W) AS W
FROM
TABLEB B
INNER JOIN TABLEA A ON B.X = A.X AND B.Y = A.Y AND B.Z = A.Z
WHERE A.W <= DATE('2013-01-01')
GROUP BY
A.X,
A.Y,
A.Z
),
Q2 AS (
SELECT
A.X,
A.Y,
A.Z,
A.W,
MAX(A.V) AS V
FROM
Q1
INNER JOIN TABLEA A ON Q1.X = A.X AND Q1.Y = A.Y AND Q1.Z = A.Z AND Q1.W = A.W
GROUP BY
A.X,
A.Y,
A.Z,
A.W
)
SELECT
B.U,
A.T
FROM
Q2
INNER JOIN TABLEA A ON Q2.X = A.X AND Q2.Y = A.Y AND Q2.Z = A.Z AND Q2.W = A.W AND Q2.V = A.V)
RIGHT OUTER JOIN TABLEB B ON Q2.X = B.X AND Q2.Y = B.Y AND Q2.Z = B.Z
... you could turn this into something appropriate for a MERGE INTO by doing the following:
remove the WITH at the top
remove the comma from the end of the Q1 block (after the closing parenthesis)
take the Q1 AS from before the opening parenthesis and put is after the ending parenthesis (remove the comma) and then put the AS in front of the Q1.
take this new Q1 block and cut it and paste it into the Q2 block after the FROM Q1 (replacing the Q1 with the query in your clipboard) NOTE: leave the other references to Q1 (in the inner join keys) alone, of course.
Now you have a bigger Q2 query. Do steps 3 and 4 again, this time replacing the Q2 (after the FROM) in your main select with the bigger Q2 query in your clipboard.
In the end, you'll have a straight SELECT query that looks like this (reformatted to show proper indentation):
SELECT
B.U,
A.T
FROM
(SELECT
A.X,
A.Y,
A.Z,
A.W,
MAX(A.V) AS V
FROM
(SELECT
A.X,
A.Y,
A.Z,
MAX(A.W) AS W
FROM
TABLEB B
INNER JOIN TABLEA A ON B.X = A.X AND B.Y = A.Y AND B.Z = A.Z
WHERE A.W <= DATE('2013-01-01')
GROUP BY
A.X,
A.Y,
A.Z) AS Q1
INNER JOIN TABLEA A ON Q1.X = A.X AND Q1.Y = A.Y AND Q1.Z = A.Z AND Q1.W = A.W
GROUP BY
A.X,
A.Y,
A.Z,
A.W) AS Q2
INNER JOIN TABLEA A ON Q2.X = A.X AND Q2.Y = A.Y AND Q2.Z = A.Z AND Q2.W = A.W AND Q2.V = A.V
RIGHT OUTER JOIN TABLEB B ON Q2.X = B.X AND Q2.Y = B.Y AND Q2.Z = B.Z
I have done this in my own personal experience (just now actually) and it works perfectly.
Good luck.

Resources