Default comparison operand in SELECT WHERE condition - abap

I have a table CategoryColours, if a category is not found, it should return the colors from the default category "*".
Example if the table contains these lines:
Category Color
* white
* black
1 red
1 blue
1 green
1 black
If I search the category "1", the query should get the 4 colors.
If I search the category "2", which has no records in the table, the query should get the 2 colors from the category "*".
Is it possible to use OpenSQL to get the exact list that I need in a single statement?
I tried with CASE and subqueries (EXIST) but I didn't manage.
It's not a stopper for my code, since I can just check if my category has records first or select my category + the default always and then remove the default if the other has records.

I think you can use UNION. I didn't try code, it can include type errors.
SELECT
category,
color
FROM CategoryColours
WHERE category = lv_category
UNION
SELECT
category,
color
FROM CategoryColours
WHERE category eq '*'
AND NOT EXISTS ( SELECT color
FROM CategoryColours
WHERE category = lv_category
)
INTO TABLE #DATA(lt_itab).

Related

SQL How to get the records where contains keywords from another table

I need help in SQL to get the records where it contains the keywords from another table
I have a main table and another table for the keywords.
Main Table
ID
Item
11
colored paper
12
red shirt
13
antiblack shoe
14
yellow desk
Keywords Table
Keywords
red
yellow
black
green
Expected Output
ID
item
12
red shirt
14
yellow desk
The expected output is the records that contains the color from the keywords table
The item with ID 11 (contain colored) and 13 (contain antiblack) are not included because it is not referred to as "color" meaning
How can I use split each item name and then match it with the keywords table to get the record only fully matched with keywords?
Thankyou!
SELECT *
FROM Main
WHERE substr(keywords,1,instr(keywords,' ')) IN(
SELECT distinct keywords FROM Keywords
)
another way
SELECT m.* FROM main AS m
LEFT JOIN keywords AS k ON m.item LIKE '%'||k.keywords||'%'
WHERE k.keywords IS NOT NULL
code above run in SQLite

SQL get first reord in each of a list of records

HELP! Kind of new to SQL. I've been working with simple statements for a few years but I need a little advanced help. I know it can be done and will save me time.
Here is my example to try to find results:
select top 1 apples, color from fruits
where apples in ('gala', 'fuji', 'granny')
and (inStock is not null and inStock <> '')
In the above query I would get the first color in 'gala' apples and thats it. What I want is the first color in 'gala', the first in 'fuji', first in 'granny' and so on.
InStock isn't as important - it's just an additional filter in the search results.
What I want is a two column list. Left Column being apple types and right column being the first color result for each apple type.
You can use row_number() window ranking function to serialize apples wise colors in a specific order. Then choose first one from each group by selecting first rows.
with cte as
(
select apples, color ,row_number()over(partition by apples order by apples) rn from fruits
where apples in ('gala', 'fuji', 'granny')
and (inStock is not null and inStock <> '')
)
select apples, color from cte where rn=1
I think one issue you might have here is the concept of "first". A color is a categorical variable and tables don't typically attach meaning to a "first" or "last" value with a few exceptions. If you're dead set on returning the first row for each fruit, one easy way to get the result utilizes union all.
SELECT top 1 apples, color from fruits where apples = 'gala'
UNION ALL
SELECT top 1 apples, color from fruits where apples = 'fuji'
UNION ALL
SELECT top 1 apples, color from fruits where apples = 'granny'

SQL - Select top item from a grouping of two columns

I have a list of numbers attached to two separate columns, and I want to just return the first "match" of the two columns to get that data. I got close with this answer, but it only works with one field. I need it to work with a combination of fields. About ten second before I was ready to post.
Here's an example table "Item":
Item Color Area
Boat Red 1
Boat Red 2
Boat Blue 4
Boat Blue 5
Car Red 3
Car Red 4
Car Blue 10
Car Blue 31
And the result set returned should be:
Item Color Area
Boat Red 1
Boat Blue 4
Car Red 3
Car Blue 10
A much simpler way to do this:
select Item,
Color,
min(Area) as Area
from Item
group by Item
Color
Just use the MIN function with a GROUP BY.
SELECT Item, Color, MIN(area) AS Area
FROM Item
GROUP BY Item, Color
Output:
Item Color Area
Boat Blue 4
Boat Red 1
Car Blue 10
Car Red 3
SQL Fiddle: http://sqlfiddle.com/#!9/46a154/1/0
SQL tables represent unordered sets. Hence, there is no "first" of anything without a column specifying the ordering.
For your example results, the simplest query is:
select item, color, min(area) as area
from item i
group by item, color;
About ten seconds before I was ready to post the question, I realized the answer:
WITH summary AS (
SELECT i.item + ':' + i.color,
a.area,
ROW_NUMBER() OVER(PARTITION BY i.item + ':' + i.color,
ORDER BY i.item + ':' + i.color DESC) AS rk
FROM Item i
group by (i.item + ':' + i.color, i.Area)
SELECT s.*
FROM summary s
WHERE s.rk = 1
It's as simple as combining the two composite key fields into one field and grouping based on that. This might be a bit hackish so if anyone wants to suggest a better option I'm all for it.

Is there such a thing as a 'IS IN' query

I have 3 tables:
Silk_Skey Name
1 Black White Checks Yellow Arms
2 Black Crimson Stripes
3 Crimson Yellow Stripes
Sub Colour Major Colour
Black Black
White White
Yellow Yellow
Crimson Red
MajorColour_Skey Major Colour
1 Black
2 White
3 Yellow
4 Red
And I want to achieve this:
ID Silk_Skey MajorColour_Skey
1 1 1
2 1 2
3 1 3
4 2 1
5 2 4
6 3 3
7 3 4
What I need to do is create a linked table matching all the colours from the 3 tables and break down the silks names so I would show 4 lines in the new table) see SQL below. My boss has advised me to use a 'IS IN' query but I have no idea what that is can you help?
SELECT s.Silks_Skey, mc.MajorColour_Skey
FROM Silks s INNER JOIN SubColour sc on sc.SubColour **'IS IN HERE'** s.SilksName
INNER JOIN MajorColour mc
ON sc.MajorColour = mc.MajorColour
You can use IN
AND table.column IN ('a','b','c')
or
AND table.column IN (1,2,3)
or if you're looking for a string like something you can do
AND table.column LIKE '%word' -- table.column ends with 'word'
AND table.column LIKE 'word%' -- table.column starts with 'word'
AND table.column LIKE '%word%' -- table.column has 'word' anywhere in the column
This is a design doomed to poor performance and awkward and painful to write queries. If your database will never be large, then it may be workable, but if it will be large, you cannot use this design structure and hope to have good performance because you will not be able to properly use indexes. Personally I would add a silk colors table related to the silks table and store the colors indivudally. One of the first rules of database design is never store more than one piece of informatino in a field. You are storing a list which always means you need a related table to have effective use of the database.
One clue to a bad (and over time usually unworkable)database design is if you need to join using functions or caluations of any type or if you need to use wildcards at the start of a phrase in a like clause. Fix this now and things will be much smoother, maintenance will take less time and performacne will be better. There is no upside to your current structure at all.
You may need to take a bit of extra time to parse and store the silk names by individual color, but the time you save in querying the database will be significant becasue you can now make use of a join and then use indexes. Search for fn_split and you will see a method of spliting the silk names into individual colors that you can use when you insert the records.
If you foolishly decide to retain the current structure, then look into using fuilltext search. It wil be faster than using a like clause with a wildcard as the first character.
For what you want to do, you need to do string manipulation because you are trying to compare one color to a list of colors in a string.
The like operator can do this. Try this on clause:
on ' '+ s.SilksName +' ' like '% '+sc.SubColour+' %'
This checks to see if a given color (sc.SubColour) in in the list (s.SilksName). For instance, if you have a list like 'RED GREEN' this will match either '%RED%' or '%GREEN%'.
The purpose of concatenating white space is to avoid partial-word matches. For instance, "blue-green" would match both "blue" and "green" without the delimiters.
The following query returns 7 rows, which seems to be correct (3 for the first row in silks and 2 for each of the other two):
with silks as (
select 1 as silks_skey, 'Black White Checks Yellow Arms' as silksname union all
select 2, 'Black Crimson Stripes' union all
select 3, 'Crimson Yellow Stripes'
),
subcolour as (
select 'black' as subcolour, 'black' as majorcolour union all
select 'white', 'white' union all
select 'yellow', 'yellow' union all
select 'crimson', 'red'
),
MajorColour as (
select 1 as MajorColour_skey, 'black' as MajorColour union all
select 2, 'white' union all
select 3, 'yellow' union all
select 4, 'red'
)
SELECT s.Silks_Skey, mc.MajorColour_Skey
FROM Silks s INNER JOIN SubColour sc on ' ' + s.SilksName + ' ' like '% ' + sc.SubColour + ' %'
INNER JOIN MajorColour mc
ON sc.MajorColour = mc.MajorColour
Sounds like what you really want to do is split the Name field on spaces and then for each one of those values which is contained in the colours table (joined on the sub-colour given that major colours are valid sub-colours too) you want one entry in a new table. Problem is that there is no intrinsic T-SQL function for splitting strings. To do that your best bet is to visit Erland Sommarskog's definitive answer on how to do this.
An alternative, and one which is not very neat and may or may not work, is to use the CONTAINS keyword in your predicate. However in order to achieve this you need to use full text indexing
and I suspect using Erland's excellent giudes on splitting strings and arrays in SQL will be more appropriate and faster.
This is the answer folks, thanks for all your ideas.
Select S.[Silks_Skey], MC.[MajorColour_Skey]
from [dbo].[Silks] S
inner join [dbo].[SubColour] SC on CHARINDEX(SC.[SubColour],S.[SilksName]) <> 0
inner join [dbo].[MajorColour] MC on SC.[MajorColour] = MC.[MajorColour]
UNION ALL
Select S.[Silks_Skey], MC.[MajorColour_Skey]
from [dbo].[Silks] S
inner join [dbo].[MajorColour] MC on CHARINDEX(MC.[MajorColour],S.[SilksName]) <> 0
ORDER BY S.[Silks_Skey]

Select unique record based on column value priority

This is a continuation of my previous question here.
In the following example:
id PRODUCT ID COLOUR
1 1001 GREEN
2 1002 GREEN
3 1002 RED
4 1003 RED
Given a product ID, I want to retrieve only one record - that with GREEN colour, if one exists, or the RED one otherwise. It sounds like I need to employ DISTINCT somehow, but I don't know how to supply the priority rule.
Pretty basic I'm sure, but my SQL skills are more than rusty..
Edit: Thank you everybody. One more question please: how can this be made to work with multiple records, ie. if the WHERE clause returns more than just one record? The LIMIT 1 would limit across the entire set, while what I'd want would be to limit just within each product.
For example, if I had something like SELECT * FROM table WHERE productID LIKE "1%" ... how can I retrieve each unique product, but still respecting the colour priority (GREEN>RED)?
try this:
SELECT top 1 *
FROM <table>
WHERE ProductID = <id>
ORDER BY case when colour ='GREEN' then 1
when colour ='RED' then 2 end
If you want to order it based on another color, you can give it in the case statement
SELECT *
FROM yourtable
WHERE ProductID = (your id)
ORDER BY colour
LIMIT 1
(Green will come before Red, you see. The LIMIT clause returns only one record)
For your subsequent edit, you can do this
select yourtable.*
from
yourtable
inner join
(select productid, min(colour) mincolour
from yourtable
where productid like '10%'
group by productid) v
on yourtable.productid=v.productid
and yourtable.colour=v.mincolour