Looking for something to replace INTERSECT in ISQL - intersect

I am trying to construct a query to handle multiple filters in ISQL. I have a query that provides the answers I need when I'm using MSSQL, but it uses the INTERSECT command, which appears not to be present in ISQL.
I have the following data:
ItemID
Label
ItemGroupID_1
ItemGroupID_2
ItemGroupID_3
1
Champagne Jean Pernet Cuvee
WI
Sparkling
White
2
Champagne Jean Pernet Millesime 2008
WI
Sparkling
White
3
Champagne Jean Pernet Millesime 2009
WI
Sparkling
White
4
Chateau de Montmirail Gigondas Cuvee 2017
WI
Still
Blue
5
Chateau de Montmirail Gigondas Cuvee 2018
WI
Still
Red
6
Chateau de Montmirail Cotes du Rhone 2015
WI
Still
Red
7
Toutigeac Bordeaux Blanc 2018
WI
Still
Black
8
Toutigeac Bordeaux Blanc 2019
WI
Still
White
9
Domaine Feuillat-Juillot Montagny 2017
WI
Null
Black
10
Domaine Alphonse Dolly Pouilly Fume 2017
WI
Still
White
I want users to be able to select from multiple filters to find, for example, all of the items where ItemGroupID_1 = 'WI', ItemGroupID_2 = 'Still', and ItemGroupID_3 = 'Red'. I have been told by the programmers that I cannot do a simple query like
SELECT ItemID from Items where ItemGroupID_1 = 'WI' and ItemGroupID_2 = 'Still' and ItemGroupID_3 = 'Red'
In MSSQL, this works:
SELECT ItemID AS A FROM Items WHERE ItemGroupID_1 = 'WI'
INTERSECT
SELECT ItemID AS A FROM Items WHERE ItemGroupID_2 = 'Still'
INTERSECT
SELECT ItemID AS A FROM Items WHERE ItemGroupID_3 = 'Red'
The problem is that the query needs to run in ISQL. And it doesn't look like ISQL has the INTERSECT command. (See Sybase's woefully inadequate technical reference.)
However, UNION does work in ISQL. Fortunately, A AND B is the same thing as NOT-A NOR NOT-B.
In MSSQL this works and returns the results I want:
SELECT ItemID, Label, ItemGroupID_1, ItemGroupID_2, ItemGroupID_3 AS A FROM Items WHERE ItemID NOT IN
(SELECT ItemID AS A FROM Items WHERE ItemGroupID_1 <> 'WI' or ItemGroupID_1 is null
UNION
SELECT ItemID AS A FROM Items WHERE ItemGroupID_2 <> 'Still' or ItemGroupID_2 is null
UNION
SELECT ItemID AS A FROM Items WHERE ItemGroupID_3 <> 'Red' or ItemGroupID_3 is null)
The subquery works in ISQL and returns everything I don't want. But when I add in the statement SELECT ItemID, Label, ItemGroupID_1, ItemGroupID_2, ItemGroupID_3 AS A FROM Items WHERE ItemID NOT IN I get:
Could not execute statement.
[UltraLite Database] Syntax error near 'UNION' [SQL Offset 132]
SQLCODE=-131, ODBC 3 State='42000'
Line 3
I came across the ISQL command EXISTS and NOT EXISTS, but NOT EXISTS returns no values.

Related

SQL query to select all tuples that do not have a specific attribute at all

I have a table (shown below) that maps users to the fruits that they like. This table is a result of a a complicated SELECT statement, and now I am left with specifying the WHERE condition.
I would like to select all users that do not like the fruit "Orange" at all.
U.id
F.fruit
1
Apple
1
Orange
1
Orange
2
Mango
3
Mango
4
Mango
4
Grapes
5
Apple
5
Orange
6
NULL
The resulting table should consist of users 2, 3, 4, 6, because users 1 and 5 have tuples that map them to "Oranges".
I am not allowed to use any sub-queries (make another SELECT statement). I am not sure how to do this given these restrictions, and I was not sure of how to google search for SQL queries for this specific situation.
I would like to select all users that do not like the fruit "Orange" at all.
Assuming you have a table of users, I would suggest not exists:
select u.*
from users u
where not exists (select 1
from <your query here> q
where q.user_id = u.user_id and q.fruit = 'orange'
);
Alternatively, you could aggregate, but this would only return users in your query:
select user_id
from <your query here>
group by user_id
having count(*) filter (where fruit = 'orange') = 0;
Or, if you showed the query, there might be other solutions as well.

SQL - Sum of two values IN a Group By

I am trying to add two values or get sum of two values and show it under one Exchange Name. Data below:
Table
--------------------------------------------------
EXCHANGE NAME CODE TURNOVER TRADEDATE
PARIS PA 12 14-NOV-2019
SWISS SW 14 14-NOV-2019
NULL SA 2 14-NOV-2019
NULL MI 2 14-NOV-2019
MILAN MI_1 3 14-NOV-2019
My Query
----------------------------------------------------
SELECT CE.EXCHANGE_NAME, sum(CE.TURNOVER)
FROM CE
WHERE CE.tradedate = '14-NOV-2019'
GROUP BY CE.EXCHANGE_NAME
Result
-----------------------------------------------------
EXCHANGE NAME SUM
PARIS 12
SWISS 14
MILAN 3
What I would like to achieve is that the total for SWISS to be 16 and MILAN to be 5 as MI belongs to MILAN also. There are NULL Values for EXCHANGE NAME but they belong to a certain exchange (Swiss in this case and Milan) i.e. code SA belongs to SWISS and MI belongs to MILAN.
How can I accommodate this in my query for situation like SWISS and MILAN where I know which code belongs to EXCHANGE_NAME?
Many thanks
You can use COALESCE():
SELECT COALESCE(CE.EXCHANGE_NAME, 'SWISS') as EXCHANGE_NAME, SUM(CE.TURNOVER)
FROM CE
WHERE CE.tradedate = '14-NOV-2019'
GROUP BY COALESCE(CE.EXCHANGE_NAME, 'SWISS');
As a note: I like the use of DATE for date constants:
WHERE CE.tradedate = DATE '2019-11-14'
This allows the use of ISO standard date formatting.
EDIT:
Use a CASE expression:
SELECT (CASE WHEN CE.CODE = 'SA' THEN 'SWISS'
WHEN CE.CODE = 'MI_1' THEN 'MILAN'
ELSE CE.EXCHANGE_NAME
END) as EXCHANGE_NAME,
SUM(CE.TURNOVER)
FROM CE
WHERE CE.tradedate = DATE '2019-11-14'
GROUP BY (CASE WHEN CE.CODE = 'SA' THEN 'SWISS'
WHEN CE.CODE = 'MI_1' THEN 'MILAN'
ELSE CE.EXCHANGE_NAME
END);
To me, it looks like you have to create a mapping table which will map codes to exchange names:
SQL> create table exmap
2 (exchange_name varchar2(20),
3 code varchar2(10));
Table created.
SQL> insert into exmap
2 select 'PARIS', 'PA' from dual union all
3 select 'SWISS', 'SW' from dual union all
4 select 'SWISS', 'SA' from dual union all
5 select 'MILAN', 'MI' from dual union all
6 select 'MILAN', 'MI_1' from dual;
5 rows created.
SQL>
Now, with date in the CE table (the one you posted), you'd join those two tables:
SQL> select e.exchange_name,
2 sum(c.turnover) sum_turnover
3 from ce c join exmap e on e.code = c.code
4 group by e.exchange_name;
EXCHANGE_NAME SUM_TURNOVER
-------------------- ------------
PARIS 12
MILAN 5
SWISS 16
SQL>
Why such an approach? Because sooner or later you'll add something like this to the CE table (so PARIS will now be 20):
SQL> insert into ce values ('PARIS', 'PR', 8);
1 row created.
Now, if you choose to maintain the mapping within the code, you'll have to fix it everywhere, in all your stored procedures, reports, forms ... whatever uses that table, and add yet another CASE, e.g.
case when code in ('PA', 'PR') then 'PARIS'
... ^^^^
this
That might drive you mad. But, if you simply add it to the mapping table:
SQL> insert into exmap values ('PARIS', 'PR');
1 row created.
the "old" join query will work without any further action:
SQL> select e.exchange_name,
2 sum(c.turnover) sum_turnover
3 from ce c join exmap e on e.code = c.code
4 group by e.exchange_name;
EXCHANGE_NAME SUM_TURNOVER
-------------------- ------------
PARIS 20
MILAN 5
SWISS 16
SQL>
You can use COALESCE() to turn NULL values of EXCHANGE_NAME to 'SWISS':
SELECT COALESCE(CE.EXCHANGE_NAME, 'SWISS'), sum(CE.TURNOVER)
FROM CE
WHERE CE.tradedate = '14-NOV-2019'
GROUP BY COALESCE(CE.EXCHANGE_NAME, 'SWISS')
Edit: you can use handy Oracle function decode() to map the code to a default EXCHANGE_NAME:
SELECT
COALESCE(
CE.EXCHANGE_NAME,
DECODE(CE.CODE, 'SA', 'SWISS', 'MI_1', 'MILAN')
) EXCHANGE,
SUM(CE.TURNOVER)
FROM CE
WHERE CE.tradedate = '14-NOV-2019'
GROUP BY COALESCE(
CE.EXCHANGE_NAME,
DECODE(CE.CODE, 'SA', 'SWISS', 'MI_1', 'MILAN')
)
You can expand the DECODE() argument as needed for your use case.

Select all except duplicate columns unless the length of the column's string is longest

I have a table with the following columns:
strWord, strWordType, strWordDescription
I'd like to be able to select all of the rows except the ones where there is a duplicate strWordDescription. In the case of duplicates, I only want to return the rows where strWord has the longest length. This should only take effect if strWordType is the same.
Notes: There are no duplicate rows of strWords/strWordType combinations only duplicate strWordDescriptions for specific strWordTypes. I would like to avoid using Distinct.
Example: myTable
strWord | strWordType | strWordDescription |
blue 2012 This is a color
blue 2014 This is a color
green 2012 This is a color
ham 2014 This is a food
chicken 2014 This is a food
Expected Results:
strWord | strWordType | strWordDescription
green 2012 This is a color
blue 2014 This is a color
chicken 2014 This is a food
Hmmm . . . a correlated subquery comes to mind:
select t.*
from t
where t.strword = (select top (1) t2.strword
from t t2
where t2.description = t.description and
t2.strWordType = t.strWordType
order by len(t2.strword) desc
);
Just solved it -
SELECT MAX(mt.strWord),
mt.strWordType,
mt.strWordDescription
FROM myTable mt
GROUP BY mt.strWordType, mt.strWordDescription
ORDER BY MAX(mt.strWord)

How to not lose records in full join

Let's say I have two tables; table A and table shown below:
A
Color ID
Blue 1
Green 2
Red 3
B
Color ID
Blue 1
Brown 2
Red 3
If I were to attempt to join them using a full join, the result would depend on which table I use in the select statement. For example the following query would produce the following result
select A.color, count(*)
from A
full join B on a.color = B.color
group by 1
order by 1
color count
Blue 1
Green 1
Red 1
1
If I decided to use B.color in the select statement instead of A.color, I would get the result below:
color count
Blue 1
Brown 1
Red 1
1
How would I get the resultset to include all values for color. I know I could accomplish using unionall, and I could use a case statement in the select statement to use one when the other is null, but is there another cleaner way to accomplish this?
Thanks,
Ben
Use coalesce to pick up the value from the other table in case the value exists in one table and not the other.
select coalesce(A.color,B.color) as color, count(*)
from A
full join B on a.color = B.color
group by 1
order by 1

MS Access query displays duplicate record that matches value in another column

I need help to query a table for distinct records by TRANS_CODE that matches value in the ITEM column. Any help would be very much appreciative.
Here is an example of the table.
ID TRANS_CODE ITEM
1 CD50 Red
2 TN30 Blue
3 RC50 Green
4 WC70 White
5 PT30 Blue
6 AB60 White
7 RC50 Red
8 WC70 Blue
9 TN30 Green
10 PT30 Green
The logic for displaying duplicate TRANS_CODE is driven by the ITEM column. The first logic for duplicate TRANS_CODE is to show ITEM that is Blue and the secord is show
ITEM that is Green. For example...
Duplicate TRANS_CODE that has ITEM, Blue, Green, and Red: Show record with Blue only
Duplicate TRANS_CODE that has ITEM, Green, Red, and White: Show record with Green only
The result should generate this list....
ID TRANS_CODE ITEM
1 CD50 Red
2 TN30 Blue
3 RC50 Green
8 WC70 Blue
5 PT30 Blue
6 AB60 White
You can map your colors to Numbers using switch. Then using two inline queries joined together you can get the answer you're looking for.
Technically you don't need to use the switch because the colors are in alpha order but just in case your real values weren't
SELECT t.id,
t.transcode,
t.item
FROM (SELECT transcode,
item,
id,
Switch([ITEM] = "Blue", 1, [ITEM] = "Green", 2, [ITEM] = "Red", 3
,
[ITEM] =
"White", 4) AS weight
FROM table2) AS t
INNER JOIN (SELECT transcode,
MIN(Switch([ITEM] = "Blue", 1, [ITEM] = "Green", 2,
[ITEM] =
"Red", 3,
[ITEM] =
"White", 4)) AS weight
FROM table2
GROUP BY transcode) AS t2
ON ( t.weight = t2.weight )
AND ( t.transcode = t2.transcode )
ORDER BY t.id
This gives you the output below, I hope that ID 8 being after 6 instead of between 3 and 5 works for you
id transcode item
1 CD50 Red
2 TN30 Blue
3 RC50 Green
5 PT30 Blue
6 AB60 White
8 WC70 Blue
Note: you can replace MIN(Switch... with FIRST(Switch... which may improve performance
You should try this:
select
(select ID from sometable as t2 where t2.trans_code = t1.trans_code and t2.item = Min(t1.Item)),
t1.trans_code,
Min(t1.item)
from sometable as t1
group by trans_code
order by 1
As you did not specify which color should have preference, I set the preference alphabetically (Blue, Green, Red and White). That's hat I could capture from your expected output.