I have a couple of tables in SQL, first one called attribute that looks like this:
SELECT 'A' AS AttributeA, 'B' AS AttributeB, 2020 AS AttributeC
UNION ALL
SELECT 'A' AS AttributeA, 'B' AS AttributeB, 2021 AS AttributeC
UNION ALL
SELECT 'B' AS AttributeA, 'C' AS AttributeB, 2022 AS AttributeC
Second table called condition looks like this:
SELECT 'IN (''A'')' AS ConditionA, 'IN (''B'')' AS ConditionB, '< 2021' AS ConditionC, 'Bad' AS Grade
UNION ALL
SELECT 'IN (''A'')' AS ConditionA, 'IN (''B'')' AS ConditionB, 'IN (2021)' AS ConditionC, 'Good' AS Grade
UNION ALL
SELECT 'NOT IN (''A'')' AS ConditionA, 'IN (''C'')' AS ConditionB, '> 2021' AS ConditionC, 'Excellent' AS Grade
The aim is to join the two together such that AttributeA satisfies ConditionA, and AttributeB satisfies ConditionB and AttributeC satisfies ConditionC etc, so that the Grade column is added onto the attribute table, like this:
If the data is good, there should never be an overlap of rules such that more than one record in the condition table satisfy the attribute record. But that's not as important right now because I can check for duplicates later.
I already have a solution which requires dynamic SQL to cycle through the condition table, build up the where clause conditions, and use that to filter records from the attributes table whilst appending the Grade column.
Another solution works similarly but vice versa, where I cycle through the distinct records of the attribute table, see which one satisfies the condition table, and start creating the record that way.
Both work, however I'm just wondering if there is a cleverer way to somehow join the two together so I don't need to use a cursor to cycle through the records individually?
You can generate one query for all conditions and chek rows in main table by one request.
From conditions table you can generate check conditions
id | ConditionA | ConditionB | ConditionC | Grade |
---|---|---|---|---|
1 | IN ('A') | IN ('B') | < 2021 | Bad |
2 | IN ('A') | IN ('B') | IN (2021) | Good |
3 | NOT IN ('A') | IN ('C') | > 2021 | Excellent |
conditions
cond |
---|
(1,case when AttributeA IN ('A') and AttributeB IN ('B') and AttributeC < 2021 then 'Bad' end) |
(2,case when AttributeA IN ('A') and AttributeB IN ('B') and AttributeC IN (2021) then 'Good' end) |
(3,case when AttributeA NOT IN ('A') and AttributeB IN ('C') and AttributeC > 2021 then 'Excellent' end) |
There condition Id (for identity) and concatenated conditions.
with static query
select *
,concat('(',Id,',case when '
,'AttributeA ',ConditionA
,' and ','AttributeB ',ConditionB
,' and ','AttributeC ',ConditionC
,' then ''', Grade,''' end)') cond
from conditions
Then generate main query
select *
from tableA a
cross apply( values
-- generated part
(1,case when AttributeA IN ('A') and AttributeB IN ('B') and AttributeC< 2021 then 'Bad' end)
,(2,case when AttributeA IN ('A') and AttributeB IN ('B') and AttributeC IN (2021) then 'Good' end)
,(3,case when AttributeA NOT IN ('A') and AttributeB IN ('C') and AttributeC > 2021 then 'Excellent' end)
-- end of generated part
)c(ConditionId,res)
where res is not null
We get rows, where complicated condition is satisfied. If attribute row satisfied many conditions, you can take all or best fit.
id | AttributeA | AttributeB | AttributeC | ConditionId | res |
---|---|---|---|---|---|
1 | A | B | 2020 | 1 | Bad |
2 | A | B | 2021 | 2 | Good |
3 | B | C | 2022 | 3 | Excellent |
Full query example
declare @sqlExpr varchar(1000);
set @sqlExpr=
'select *
from tableA a
cross apply( values '+CHAR(13)
+(select string_agg(
concat(' (',Id,',','case when ','AttributeA ',ConditionA,' and ','AttributeB ',ConditionB,' and ','AttributeC ',ConditionC,' then ''',Grade,''' end) ')
,','+ CHAR(13) )cond
from conditions
)
+'
)c(ConditionId,res)
where res is not null'
;
exec (@sqlExpr);
@sqlExpr is
select *
from tableA a
cross apply( values
(1,case when AttributeA IN ('A') and AttributeB IN ('B') and AttributeC < 2021 then 'Bad' end) ,
(2,case when AttributeA IN ('A') and AttributeB IN ('B') and AttributeC IN (2021) then 'Good' end) ,
(3,case when AttributeA NOT IN ('A') and AttributeB IN ('C') and AttributeC > 2021 then 'Excellent' end)
)c(ConditionId,res)
where res is not null
This example is for SQL Server (cross apply).
You can use cross join lateral
for PostgreSql and other similar method in your DBMS.