Store mapping with multiple conditions in database - sql

I have a SELECT-Statement where ich have to map keys. I Need to store this mapping an a database because this mapping can Change. As the mapping-condition is only base on one key, it is relativly simple.
SELECT
table.flield1 AS COL1
, (SELECT value from TransformationTable WHERE key = table.field2) AS COL2
[...]
Now i have a case where the mapping-condition is more complicated. In SQL it is like:
CASE
WHEN table.field1 = 'ORG' AND table.field2 IN (1,2,3) THEN 01
WHEN table.field1 = 'ORG' AND table.field2 NOT IN (5,76,88) OR IN (9) THEN 02
WHEN table.field1 != 'ORG' AND table.field2 IN (1,2,3) THEN 03
END
How can I store such condition in a database so that I can select the value like in example 1.
Does some one have an idea?

Essentially to perform this task you need something that can evaluate an expression for you on the fly. The only ways I have solved this kind of problem in the past are:
Easy to implement but less flexible: embed the expression in a view then query from the view; change view as mapping expression changes. Remember a view is just a stored SQL statement and a SQL statement can contain and evaluate complex expressions.
Harder to implement but more flexible: store a lexical construct for the expression in a table somewhere; then read in that "expression" and use it to generate the SQL dynamically; then run the dynamically generated SQL.
Have fun!!!

If You want a table solution, then I think of something like this:
PARENT | CHILD | LOGICAL | COLUMN | OPERATOR | VALUES | OUTPUT |
OPERATOR ID | OPERATOR ID | OPERATOR | (expression) | OPERATOR | VALUES | ATTRIBUTE |
--------------------------- ---------------------------------------------------------------
10001 | 10003 | AND | field1 | IN | 1,2,3 | 01 |
10001 | 10004 | AND | field2 | NOT IN | 5,76,88 | 01 |
10002 | 10005 | | field1 | IN | ... | 02 |
10002 | 10006 | | field2 | NOT IN | ... | 02 |
Columns PARENT OPERATOR ID and CHILD OPERATOR ID are additional, in case You need nested AND-OR operators ( ... AND ( ... OR ... AND ( ... OR ... ) ) )

Related

Casting string to int i.e. the string "res"

I have a column in a table which is type array<string>. The table is partitioned daily since 2018-01-01. At some stage, the values in the array goes from strings to integers. The data looks like this:
| yyyy_mm_dd | h_id | p_id | con |
|------------|-------|------|---------------|
| 2018-10-01 | 52988 | 1 | ["res", "av"] |
| 2018-10-02 | 52988 | 1 | ["1","2"] |
| 2018-10-03 | 52988 | 1 | ["1","2"] |
There is a mapping between the strings and integers. "res" maps to 1 and "av" maps to 2 etc. However, I've written a query to perform some logic. Here is a snippet (subquery) of it:
SELECT
t.yyyy_mm_dd,
t.h_id,
t.p_id,
CAST(e.con AS INT) AS api
FROM
my_table t
LATERAL VIEW EXPLODE(con) e AS con
My problem is that this doesn't work for the earlier dates when strings were used instead of integers. Is there anyway to select con and remap the strings to integers so the data is across all partitions?
Expected output:
| yyyy_mm_dd | h_id | p_id | con |
|------------|-------|------|---------------|
| 2018-10-01 | 52988 | 1 | ["1","2"] |
| 2018-10-02 | 52988 | 1 | ["1","2"] |
| 2018-10-03 | 52988 | 1 | ["1","2"] |
Once the values selected are all integers (within a string array), then the CAST(e.con AS INT) will work
Edit: To clarify, I will put the solution as a subquery before I use lateral view explode. This way I am exploding on a table where all partitions have integers in con. I hope this makes sense.
CAST(e.api as INT) returns NULL if not possible to cast. collect_list will collect an array including duplicates and without NULLs. If you need array without duplicated elements, use collect_set().
SELECT
t.yyyy_mm_dd,
t.h_id,
t.p_id,
collect_list(--array of integers
--cast case as string if you need array of strings
CASE WHEN e.api = 'res' THEN 1
WHEN e.api = 'av' THEN 2
--add more cases
ELSE CAST(e.api as INT)
END
) as con
FROM
my_table t
LATERAL VIEW EXPLODE(con) e AS api
GROUP BY t.yyyy_mm_dd, t.h_id, t.p_id

Why is this Query not Updateable?

I was looking to provide an answer to this question in which the OP has two tables:
Table1
+--------+--------+
| testID | Status |
+--------+--------+
| 1 | |
| 2 | |
| 3 | |
+--------+--------+
Table2
+----+--------+--------+--------+
| ID | testID | stepID | status |
+----+--------+--------+--------+
| 1 | 1 | 1 | pass |
| 2 | 1 | 2 | fail |
| 3 | 1 | 3 | pass |
| 4 | 2 | 1 | pass |
| 5 | 2 | 2 | pass |
| 6 | 3 | 1 | fail |
+----+--------+--------+--------+
Here, the OP is looking to update the status field for each testID in Table1 with pass if the status of all stepID records associated with the testID in Table2 have a status of pass, else Table1 should be updated with fail for that testID.
In this example, the result should be:
+--------+--------+
| testID | Status |
+--------+--------+
| 1 | fail |
| 2 | pass |
| 3 | fail |
+--------+--------+
I wrote the following SQL code in an effort to accomplish this:
update Table1 a inner join
(
select
b.testID,
iif(min(b.status)=max(b.status) and min(b.status)='pass','pass','fail') as v
from Table2 b
group by b.testID
) c on a.testID = c.testID
set a.testStatus = c.v
However, MS Access reports the all-too-familiar, 'operation must use an updateable query' response.
I know that a query is not updateable if there is a one-to-many relationship between the record being updated and the set of values, but in this case, the aggregated subquery would yield a one-to-one relationship between the two testID fields.
Which left me asking, why is this query not updateable?
You're joining in a query with an aggregate (Max).
Aggregates are not updateable. In Access, in an update query, every part of the query has to be updateable (with the exception of simple expressions, and subqueries in WHERE part of your query), which means your query is not updateable.
You can work around this by using domain aggregates (DMin and DMax) instead of real ones, but this query will take a large performance hit if you do.
You can also work around it by rewriting your aggregates to take place in an EXISTS or NOT EXISTS clause, since that's part of the WHERE clause thus doesn't need to be updateable. That would likely minimally affect performance, but means you have to split this query in two: 1 query to set all the fields to "pass" that meet your condition, another to set them to "fail" if they don't.

Select datas and fieldname from multiple tables based on same id

I have tried to find the best solution for my "problem" but my SQL knowledge is limited so I'm asking for your help.
I currently have a "main" table named Project and its primary key (ProjectId) is use in other tables like "Description" (see below)
Table "Project" :
+-----------+----------+-----------+
| ProjectId | P_Field1 | P_Field2 |
+-----------+----------+-----------+
| 1 | val1 | val2 |
+-----------+----------+-----------+
Table "Descriptive" :
+-----------+----------+-----------+
| ProjectId | D_Field1 | D_Field2 |
+-----------+----------+-----------+
| 1 | valA | valB |
+-----------+----------+-----------+
I'm trying to write a stored procedure that selects all the datas with its fieldname of multiple tables, based on a "ProjectId", then the output result would be :
Sotred Procedure Result with parameter #ProjectId = 1 :
+-----------+----------+
| FieldName | Value |
+-----------+----------+
| P_Field1 | val1 |
+-----------+----------+
| P_Field2 | val2 |
+-----------+----------+
| D_Field1 | valA |
+-----------+----------+
| D_Field2 | valB |
+-----------+----------+
A friend told me that the SQL "PIVOT" function could help me but I never have used this function and my search on it really didn't help me because it is often used with "SUM", "MAX" etc and I don't need that.
Thanks in advance.
You want unpivot, not pivot, and a union
select fieldname, value from [project]
unpivot (value for fieldname in ([p_field1],[p_field2])) p
where projectid=1
union all
select fieldname, value from [descriptive]
unpivot (value for fieldname in ([d_field1],[d_field2])) p
where projectid=1

How to get records from a table where some field's value is in camel-case

I have a table like this,
+----+-----------+
| Id | Value |
+----+-----------+
| 1 | ABC_DEF |
| 31 | AcdEmc |
| 44 | AbcDef |
| 2 | BAA_CC_CD |
| 55 | C_D_EE |
+----+-----------+
I need a query to get the records which Value is only in camelcase (ex: AcdEmc, AbcDef etc. not ABC_DEF).
Please note that this table has only these two types of string values.
You can use UPPER() for this
select * from your_table
where upper(value) <> value COLLATE Latin1_General_CS_AS
If your default collation is case-insensitive you can force a case-sensitive collation in your where clause. Otherwise you can remove that part from your query.
Based on the sample data, the following will work. I think the issue we're dealing with is checking whether the string contains underscores.
SELECT * FROM [Foo]
WHERE Value NOT LIKE '%[_]%';
See Fiddle
UPDATE: Corrected error. I forgot '_' meant "any character".

Unique string table in SQL and replacing index values with string values during query

I'm working on an old SQL Server database that has several tables that look like the following:
|-------------|-----------|-------|------------|------------|-----|
| MachineName | AlarmName | Event | AlarmValue | SampleTime | ... |
|-------------|-----------|-------|------------|------------|-----|
| 3 | 180 | 8 | 6.780 | 2014-02-24 | |
| 9 | 67 | 8 | 1.45 | 2014-02-25 | |
| ... | | | | | |
|-------------|-----------|-------|------------|------------|-----|
There is a separate table in the database that only contains unique strings, as well as the index for each unique string. The unique string table looks like this:
|----------|--------------------------------|
| Id | String |
|----------|--------------------------------|
| 3 | MyMachine |
| ... | |
| 8 | High CPU Usage |
| ... | |
| 67 | 404 Error |
| ... | |
|----------|--------------------------------|
Thus, when we want to get something out of the database, we get the respective rows out, then lookup each missing string based on the index value.
What I'm hoping to do is to replace all of the string indexes with the actual values in a single query without having to do post-processing on the query result.
However, I can't figure out how to do this in a single query. Do I need to use multiple JOINs? I've only been able to figure out how to replace a single value by doing something like -
SELECT UniqueString.String AS "MachineName" FROM UniqueString
JOIN Alarm ON Alarm.MachineName = UniqueString.Id
Any help would be much appreciated!
Yes, you can do multiple joins to the UniqueStrings table, but change the order to start with the table you are reporting on and use unique aliases for the joined table. Something like:
SELECT MN.String AS 'MachineName', AN.String as 'AlarmName' FROM Alarm A
JOIN UniqueString MN ON A.MachineName = MN.Id
JOIN UniqueString AN ON A.AlarmName = AN.Id
etc for any other columns