Use second condition instead if rows are not found - abap

I have the following select:
SELECT name, text, lang FROM texts
WHERE name IN #r_names
AND lang IN ( #lv_lang, 'E' )
INTO TABLE #DATA(lt_texts).
It will select texts multiple lines of texts for a given name.
How do I say that I want texts with lang = lv_lang, but if they don't exist, then select ones with lang = 'E' all within one request to the DB and no processing on the application side?

You can use UNION operator for this task:
SELECT name, text, lang FROM texts
WHERE name IN #r_names
AND lang = #lv_lang
UNION
SELECT name, text, lang FROM texts
WHERE name IN #r_names
AND lang = 'E'
AND NOT EXISTS ( SELECT name
FROM texts
WHERE name IN #r_names
AND lang = #lv_lang
)
INTO TABLE #DATA(lt_texts).

I like coalesce for this sort of thing (it will fill-in your target with the first non-null value). You can have sy-langu as default and more languages in order of precedence:
SELECT SINGLE coalesce( default~eqktx, greek~eqktx, english~eqktx )
FROM equi AS e LEFT OUTER JOIN eqkt AS default
ON default~equnr = e~equnr
AND default~spras = #sy-langu
LEFT OUTER JOIN eqkt AS greek
ON greek~equnr = e~equnr
AND greek~spras = 'G'
LEFT OUTER JOIN eqkt AS english
ON english~equnr = e~equnr
AND english~spras = 'E'
WHERE e~equnr = #ls_equi-equnr
INTO #DATA(lv_eqktx).
Your example would become:
SELECT coalesce( default~name, english~name ),
coalesce( default~text, english~text ),
coalesce( default~lang, english~lang )
FROM texts AS default LEFT OUTER JOIN texts AS english
ON english~name = default~name
AND english~lang = 'E'
WHERE default~name IN #r_names
AND default~lang = #lv_lang
INTO TABLE #DATA(lt_texts).

Based on Suncatcher answer but without the UNION. Didn't tried, so I don't know if it works as expected:
SELECT name, text, lang FROM texts
WHERE name IN #r_names
AND ( lang = #lv_lang
OR ( NOT EXISTS ( SELECT name
FROM texts
WHERE name IN #r_names
AND lang = #lv_lang
)
AND lang = 'E'
)
)
INTO TABLE #DATA(lt_texts).

Related

How to check the field is empty or not based on alias

I am writing a query for getting a customer list from database. The query contains a where condition to check customer's email is empty or not.
The original query:
SELECT ID
FROM MAINTABLE m
LEFT JOIN CUSTOMER c
ON m.CUS = c.CUS
WHERE c.EMAIL <> ''
Howevery, in my case. delivery_type is a SSRS parameters and used to represent the email field is empty or not
. If the value is 'A' means the email field is empty, 'B' is not. I have tried below query but it seems totally wrong.
Anyone can help me on this issue?
SELECT ID, CASE WHEN c.EMAIL = '' then 'A' WHEN c.EMAIL <> '' THEN 'B' END 'delivery_type'
FROM MAINTABLE m
LEFT JOIN CUSTOMER c
ON m.CUS = c.CUS
WHERE delivery_type = #delivery_type
WHERE delivery_type = #delivery_type AND (
(#delivery_type = 'A' AND email = '')
OR
(#delivery_type = 'B' AND email <> '')
)

Trying to understand a query (LEFT JOIN and subquery)

I've been trying to understand the behavior of a query but i dont fully understand what is going on.
Take a look:
SELECT main.entity_id,main.sku,name.value AS name
FROM product_entity AS main
LEFT JOIN product_entity_varchar AS name ON main.entity_id = name.entity_id
WHERE name.attribute_id = (
SELECT attribute_id
FROM ger_attribute
WHERE attribute_code LIKE "name"
AND 'entity_type_id' = (
SELECT entity_type_id
FROM ger_entity_type
WHERE entity_type_code = 'catalog_product_info'
)
)
Can you please explain why it is using a subquery ,why the LEFT JOIN is important in these cases and the condition entity_type_code = 'catalog_product_info'?
Thanks
SELECT main.entity_id,main.sku,name.value AS name
FROM product_entity AS main
LEFT JOIN product_entity_varchar AS name ON main.entity_id = name.entity_id
...
The query starts by pulling out table product_entity. The LEFT JOIN allows the query to access the record(s) in table product_entity_varchar whose entity_id is equal to the value of the column that has the same name in product_entity.
In the resultset, the value of column value from table product_entity_varchar is displayed, under alias name.
The keyword LEFT makes the relation optional ; it there is no matching record in product_entity_varchar, the name will simply appear as NULL in the output. If it were an [INNER] JOIN, then the relation would be mandatory : unmatched records would be filtered out, and would not appear in the output.
OK. Let's clarify what is LEFT OUTER JOIN in SQL.
LEFT JOIN queries could be understood as just syntax abbreviation of UNION ALL of INNER JOIN-ed and NOT EXIST-ed queries.
Let's decompose LEFT JOIN-ed query:
SELECT main.entity_id,main.sku,name.value AS name
FROM
product_entity AS main
LEFT JOIN product_entity_varchar AS name ON main.entity_id = name.entity_id
WHERE
name.attribute_id = (
SELECT attribute_id
FROM ger_attribute
WHERE attribute_code LIKE "name"
AND 'entity_type_id' = (
SELECT entity_type_id
FROM ger_entity_type
WHERE entity_type_code = 'catalog_product_info'
)
)
Is equivalent to:
SELECT main.entity_id,main.sku,name.value AS name
FROM
product_entity AS main
JOIN product_entity_varchar AS name
ON main.entity_id = name.entity_id
WHERE
name.attribute_id = (
SELECT attribute_id
FROM ger_attribute
WHERE attribute_code LIKE "name"
AND 'entity_type_id' = (
SELECT entity_type_id
FROM ger_entity_type
WHERE entity_type_code = 'catalog_product_info'
)
)
UNION ALL
SELECT main.entity_id,main.sku, NULL AS name -- <-- Attention!
FROM
product_entity AS main
NOT EXISTS(
SELECT * FROM product_entity_varchar AS name
WHERE
main.entity_id = name.entity_id
AND name.attribute_id = (
SELECT attribute_id
FROM ger_attribute
WHERE attribute_code LIKE "name"
AND 'entity_type_id' = (
SELECT entity_type_id
FROM ger_entity_type
WHERE entity_type_code = 'catalog_product_info'
)
)
)
I hope this will help to understand query, although it's impossible without data to answer "why it is using a subquery" etc.

Get values for a reference where a key exists

I have a table that looks like this:
ref key val
--------------------------
1 Include Yes
1 Color Green
1 Shape Square
2 Include No
2 Color Red
2 Shape Circle
If an Include key exists with value Yes, I'd like to get all the values with the same ref.
So for the above example the result should be:
ref key val
--------------------------
1 Include Yes
1 Color Green
1 Shape Square
This is what I have so far:
select *
from ref_table
where ref in
(
select ref
from ref_table
where key = 'Include' and val = 'Yes'
)
This also seems to work:
with included
as
(
select ref
from ref_table
where key = 'Include' and val = 'Yes'
)
select *
from ref_table
where ref in
(
select * from included
)
Just wondering if there is a better (simpler) way to do this.
You can use EXISTS() :
SELECT * FROM ref_table t
WHERE EXISTS(SELECT 1 FROM ref_table s
WHERE t.ref = s.ref and s.key = 'Include' and s.val = 'Yes')
I always prefer this method over IN() , most of the time it performs better (exists wait for the first record to return) , it is also more clear for that purpose. IN() can also have problems when it can return NULL values.
Another way is an INNER JOIN :
SELECT t.* FROM ref_table t
INNER JOIN ref_table s
ON(t.ref = s.ref and s.key = 'Include' and s.val = 'Yes')
One more way with OUTER APPLY, but I guess it is not much simpler:
SELECT r.*
FROM ref_table r
OUTER APPLY (
SELECT DISTINCT ref
FROM ref_table
WHERE [key] = 'Include' and val = 'Yes') p
WHERE p.ref = r.ref

How to use two expression in a sub query

I have two table
MARKS:
Roll
SubA (contains subject code)
SubB (contains subject code)
SubC (contains subject code)
....
....
SUBJECT:
Sub_Code
Sub_Name
Sub_Opt (contains option like theory / practical)
When I write this query
Select
Roll,
(Select Sub_Name From Subject Wwhere dbo.marks.subA = dbo.subject.sub_code),
(Select Sub_Name From Subject where dbo.marks.SubB = dbo.subject.sub_code)
from marks, subject
it runs successfully, but when I try to add subject option within subquery Like -
select
Roll,
( SELECT Sub_name ,
Sub_Opt
FROM subject
WHERE dbo.marks.SubA = dbo.subject.Sub_Code
)
( SELECT Sub_name ,
Sub_Opt
FROM subject
WHERE dbo.marks.SubB = dbo.subject.Sbu_Code
)
from Marks, subject
it causes an error:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
I want result in this format
ROLL, SUB_NAME_A, SUB_OPT_A, SUB_NAME_B, SUB_OPT_B, SUB_NAME_C, SUB_OP_C,......
You could join on the tables, you might need a LEFT JOIN in place of inner if there's no guarantee of data in the SUBJECT table
SELECT m.Roll ,
a.Sub_name ,
a.Sub_Opt ,
b.Sub_name ,
b.Sub_Opt
FROM marks m
INNER JOIN SUBJECT a ON m.SubA = a.Sub_Code
INNER JOIN SUBJECT b ON m.SubB = b.Sub_Code
etc
select
Roll,
( SELECT Sub_name
FROM subject
WHERE dbo.marks.SubA = dbo.subject.Sub_Code
) SUB_NAME_A ,
( SELECT Sub_Opt
FROM subject
WHERE dbo.marks.SubA = dbo.subject.Sub_Code
) SUB_OPT_A ,
( SELECT Sub_name
FROM subject
WHERE dbo.marks.SubB = dbo.subject.Sub_Code
) SUB_NAME_B ,
( SELECT Sub_Opt
FROM subject
WHERE dbo.marks.SubB = dbo.subject.Sub_Code
) SUB_OPT_B ,
from Marks
this also should work

SQL Elaborate Joins Query

I'm trying to solve the below problem.
I feel like it is possible, but I can't seem to get it.
Here's the scenario:
Table 1 (Assets)
1 Asset-A
2 Asset-B
3 Asset-C
4 Asset-D
Table 2 (Attributes)
1 Asset-A Red
2 Asset-A Hard
3 Asset-B Red
4 Asset-B Hard
5 Asset-B Heavy
6 Asset-C Blue
7 Asset-C Hard
If I am looking for something having the same attributes as Asset-A, then it should identify Asset-B since Asset-B has all the same attributes as Asset-A (it should discard heavy, since Asset-A didn't specify anything different or the similar). Also, if I wanted the attributes for only Asset-A AND Asset-B that were common, how would I get that?
Seems simple, but I can't nail it...
The actual table I am using, is almost precisely Table2, simply an association of an AssetId, and an AttributeId so:
PK: Id
int: AssetId
int: AttributeId
I only included the idea of the asset table to simplify the question.
SELECT ato.id, ato.value
FROM (
SELECT id
FROM assets a
WHERE NOT EXISTS
(
SELECT NULL
FROM attributes ata
LEFT JOIN
attributes ato
ON ato.id = ata.id
AND ato.value = ata.value
WHERE ata.id = 1
AND ato.id IS NULL
)
) ao
JOIN attributes ato
ON ato.id = ao.id
JOIN attributes ata
ON ata.id = 1
AND ata.value = ato.value
, or in SQL Server 2005 (with sample data to check):
WITH assets AS
(
SELECT 1 AS id, 'A' AS name
UNION ALL
SELECT 2 AS id, 'B' AS name
UNION ALL
SELECT 3 AS id, 'C' AS name
UNION ALL
SELECT 4 AS id, 'D' AS name
),
attributes AS
(
SELECT 1 AS id, 'Red' AS value
UNION ALL
SELECT 1 AS id, 'Hard' AS value
UNION ALL
SELECT 2 AS id, 'Red' AS value
UNION ALL
SELECT 2 AS id, 'Hard' AS value
UNION ALL
SELECT 2 AS id, 'Heavy' AS value
UNION ALL
SELECT 3 AS id, 'Blue' AS value
UNION ALL
SELECT 3 AS id, 'Hard' AS value
)
SELECT ato.id, ato.value
FROM (
SELECT id
FROM assets a
WHERE a.id <> 1
AND NOT EXISTS
(
SELECT ata.value
FROM attributes ata
WHERE ata.id = 1
EXCEPT
SELECT ato.value
FROM attributes ato
WHERE ato.id = a.id
)
) ao
JOIN attributes ato
ON ato.id = ao.id
JOIN attributes ata
ON ata.id = 1
AND ata.value = ato.value
I don't completely understand the first part of your question, identifying assets based on their attributes.
Making some assumptions about column names, the following query would yield the common attributes between Asset-A and Asset-B:
SELECT [Table 2].Name
FROM [Table 2]
JOIN [Table 1] a ON a.ID = [Table 2].AssetID AND a.Name = 'Asset-A'
JOIN [Table 1] b ON b.ID = [Table 2].AssetID AND b.Name = 'Asset-B'
GROUP BY [Table 2].Name
Select * From Assets A
Where Exists
(Select * From Assets
Where AssetId <> A.AssetID
And (Select Count(*)
From Attributes At1 Join Attributes At2
On At1.AssetId <> At2.AssetId
And At1.attribute <> At2.Attribute
Where At1.AssetId = A.AssetId Asset) = 0 )
And AssetId = 'Asset-A'
select at2.asset, count(*)
from attribute at1
inner join attribute at2 on at1.value = at2.value
where at1.asset = "Asset-A"
and at2.asset != "Asset-A"
group by at2.asset
having count(*) = (select count(*) from attribute where asset = "Asset-A");
Find all assets who have every attribute that "A" has (but also may have additional attributes):
SELECT Other.ID
FROM Assets Other
WHERE
Other.AssetID <> 'Asset-A' -- do not return Asset A as a match to itself
AND NOT EXISTS (SELECT NULL FROM Attributes AttA WHERE
AttA.AssetID='Asset-A'
AND NOT EXISTS (SELECT NULL FROM Attributes AttOther WHERE
AttOther.AssetID=Other.ID AND AttOther.AttributeID = AttA.AttributeID
)
)
I.e., "find any asset where there is no attribute of A that is not also an attribute of this asset".
Find all assets who have exactly the same attributes as "A":
SELECT Other.ID
FROM Assets Other
WHERE
Other.AssetID <> 'Asset-A' -- do not return Asset A as a match to itself
AND NOT EXISTS (SELECT NULL FROM Attributes AttA WHERE
AttA.AssetID='Asset-A'
AND NOT EXISTS (SELECT NULL FROM Attributes AttOther WHERE
AttOther.AssetID=Other.ID
AND AttOther.AttributeID = AttA.AttributeID
)
)
AND NOT EXISTS (SELECT NULL FROM Attributes AttaOther WHERE
AttaOther.AssetID=Other.ID
AND NOT EXISTS (SELECT NULL FROM Attributes AttaA WHERE
AttaA.AssetID='Asset-A'
AND AttaA.AttributeID = AttaOther.AttributeID
)
)
I.e., "find any asset where there is no attribute of A that is not also an attribute of this asset, and where there is no attribute of this asset that is not also an attribute of A."
This solution works as prescribed, thanks for the input.
WITH Atts AS
(
SELECT
DISTINCT
at1.[Attribute]
FROM
Attribute at1
WHERE
at1.[Asset] = 'Asset-A'
)
SELECT
DISTINCT
Asset,
(
SELECT
COUNT(ta2.[Attribute])
FROM
Attribute ta2
INNER JOIN
Atts b
ON
b.[Attribute] = ta2.[attribute]
WHERE
ta2.[Asset] = ta.Asset
)
AS [Count]
FROM
Atts a
INNER JOIN
Attribute ta
ON
a.[Attribute] = ta.[Attribute]
Find all assets that have all the same attributes as asset-a:
select att2.Asset from attribute att1
inner join attribute att2 on att2.Attribute = att1.Attribute and att1.Asset <> att2.Asset
where att1.Asset = 'Asset-A'
group by att2.Asset, att1.Asset
having COUNT(*) = (select COUNT(*) from attribute where Asset=att1.Asset)
I thought maybe I can do this with LINQ and then work my way backwards with:
var result = from productsNotA in DevProducts
where productsNotA.Product != "A" &&
(
from productsA in DevProducts
where productsA.Product == "A"
select productsA.Attribute
).Except
(
from productOther in DevProducts
where productOther.Product == productsNotA.Product
select productOther.Attribute
).Single() == null
select new {productsNotA.Product};
result.Distinct()
I thought that translating this back to SQL with LinqPad would result into a pretty SQL query. However it didn't :). DevProducts is my testtable with a column Product and Attribute. I thought I'd post the LINQ query anyways, might be useful to people who are playing around with LINQ.
If you can optimize the LINQ query above, please let me know (it might result in better SQL ;))
I'm using following DDL
CREATE TABLE Attributes (
Asset VARCHAR(100)
, Name VARCHAR(100)
, UNIQUE(Asset, Name)
)
Second question is easy
SELECT Name
FROM Attributes
WHERE Name IN (SELECT Name FROM Attributes WHERE Asset = 'A')
AND Asset = 'B'
First question is not more difficult
SELECT Asset
FROM Attributes
WHERE Name IN (SELECT Name FROM Attributes WHERE Asset = 'A')
GROUP BY Asset
HAVING COUNT(*) = (SELECT COUNT(*) FROM FROM Attributes WHERE Asset = 'A')
Edit:
I left AND Asset != 'A' out of the WHERE clause of the second snippet for brevity