If Condition in Oracle SQL - sql

I am working on a legacy system, and many of the database structures are horrendous (key / value pairs).
I have the following select statement:
(
SELECT D.F_VALUE
FROM T_WEB_QUOTES_DATA D
WHERE
D.F_QUOTE_ID = TO_CHAR(VR_RENTAL.QUOTEID)
AND D.F_KEY = 'Secondary_Driver_Forename'
) AS "SECONDARY_DRIVER_FORENAME"
So as you can see it is looking for a record where the F_Key column has a value of Secondary_Driver_Forename. The problem is there is another F_Key that holds the same exact information and I need to check for both keys.
So what I want to do is:
If there are no records where F_Key = Secondary_Driver_Forename or of such a record exists, but the value is an empty string or null, then I would like to go and look for a record where the F_Key is 2ndary_Driver_FirstName and if that does not exist (or is null), I would like to return a string saying No Key
How can I achieve this in Oracle SQL?

I am thinking of something like this:
(
SELECT (case when max(case when D.F_KEY in 'Secondary_Driver_Forename' then 1 else 0 end) = 1
then max(case when D.F_KEY in 'Secondary_Driver_Forename' then D.F_VALUE end)
else max(D.F_Value)
end)
FROM T_WEB_QUOTES_DATA D
WHERE
D.F_QUOTE_ID = TO_CHAR(VR_RENTAL.QUOTEID)
AND D.F_KEY in ('Secondary_Driver_Forename', '2ndary_Driver_FirstName')
) AS "SECONDARY_DRIVER_FORENAME";
That is, do a conditional aggregation of the values. If the primary value is present, then use it. Otherwise, just choose the value that is there (either NULL or the value from the second key).

Related

INPUT VALUE depending on the table rows

I want to input a new row in a table with the following design
CREATE TABLE DMZ
(
DDM date NOT NULL,
NDM int NOT NULL,
PR int NOT NULL
CONSTRAINT PK_DMZ PRIMARY KEY(NDM)
);
PR can only be 1, or 2, which I defined as a constraint.(1 if this document is for income, and 2 if this document is a consumption. DM is a document number (actually Id in my case).
ALTER TABLE DMZ
ADD CONSTRAINT PR CHECK (PR IN (1,2));
I filled it with some handwritten data
INSERT INTO DMZ VALUES('2014.01.04', 20, 1);
INSERT INTO DMZ VALUES('2014.01.04', 21, 1);
INSERT INTO DMZ VALUES('2014.01.04', 22, 2);
There are two rows, where PR = 1, and only one where PR = 2. I want to write a script to INSERT a new row like this
INSERT INTO DMZ(DDM, PR) VALUES(GETDATE(), X)
Where X, I want to have something like "count rows where PR = 1 and rows where PR = 2, and if there more rows where PR = 1, use PR = 2 in newly inserted row, and if there are more rows where PR = 2, use PR = 1.
P.S.: That is a recreation of my deleted answer, hope now it's clear. To those who asked, why am I doing such a nonsence - it is a part of a list of tasks I HAVE to perform. I tried to do it, but I don't know how to perform this part with PR.
EDIT: I managed to write what I needed, but I am getting the following error ""Cannot perform an aggregate function on an expression containing an aggregate or a subquery."
INSERT INTO DMZ(ddm, pr)
SELECT COUNT(CASE WHEN (COUNT(CASE WHEN PR = 1 THEN 1 ELSE 0 END)> COUNT(CASE WHEN PR = 2 THEN 1 ELSE 0 END)) THEN 1 ELSE 2 END) AS pr, GETDATE() as ddm
FROM DMZ
Try doing a INSERT SELECT statement with a CASE statement to check your PR counts using SUM and CASE in a subquery:
INSERT INTO DMZ (a.DDM, a.NDM, a.PR)
SELECT GETDATE() AS DOM,
a.NDM AS NDM,
CASE WHEN a.PR_1_Count > a.PR_2_Count
THEN 2
ELSE 1
END AS PR
FROM (SELECT
MAX(NDM) + 1 AS NDM,
SUM(CASE WHEN PR = 1 THEN 1 ELSE 0 END) AS PR_1_Count,
SUM(CASE WHEN PR = 2 THEN 1 ELSE 0 END) AS PR_2_Count
FROM DMZ) a
Fiddle here.
Note: If you want an actual count to be inserted, remove your CONSTRAINT for the PR check and change the CASE statement from THEN 2 to THEN PR_2_Count and THEN 1 to THEN PR_1_Count.
Also, I've hardcoded a NDM column value in my demo because you're column is set to NOT NULL, I assume you'll handle that.
Update: Per your comment below, I've updated the syntax to include MAX(NDM) + 1. I would, however, suggest adding a new NDM IDENTITY column to replace your current NDM column so that it will generate your PK for you vs. generating the value yourself (see the attached Fiddle for an example of this). Read more about IDENTITY columns here and how to do it here.
Identity columns can be used for generating key values. The identity
property on a column guarantees the following:
Each new value is generated based on the current seed & increment.
Each new value for a particular transaction is different from other
concurrent transactions on the table.
The identity property on a column does not guarantee the following:
Uniqueness of the value - Uniqueness must be enforced by using a
PRIMARY KEY or UNIQUE constraint or UNIQUE index.

finding int_ids that have more than one row with a flag set to Y

I have a table that can store multiple descriptions for each code. However there is a flag in that table that is to indicate which of those is the main or primary description. In some instances, we have codes that have more than one with this flag set to Y which is not correct.
I am having trouble coming up with the SQL to get all the rows in that table that have more than one description set to Y.
I've used this SQL to identify rows that do not have ANY dsp_fg = 'Y'
select *
from table A
where dsp_fg = 'N'
and not exists (select 1 FROM table where cod_int_id = A.cod_int_id AND dsp_fg = 'Y')
But I am having trouble writing the SQL to get me the cod_int_ids that have more than one Y record, can someone help?
SELECT int_id FROM A
WHERE dsp_fg = 'Y'
GROUP BY int_id
HAVING count(1) > 1
This is not perfect, but it identifies what I need.

Unable to retrieve NULL data

I have three fields Category, Date, and ID. I need to retrieve data that does not belong under certain ID. Here is an example of my query:
SELECT Category, Date, ID
FROM table
WHERE ID NOT IN('1','2','3')
AND Date = '01/06/2015'
After running this query I should only get records that do not have any ID meaning NULL values because for yesterday's record only ID 1,2,3 exist and rest do not have any value (NULL). For some reason when I run the query it takes away the NULL values as well so I end up with 0 rows. This is very stranger to me and I do not understand what is the cause. All I know that the ID numbers are string values. Any suggestions?
Try this. NULL values cannot not be equated to anything else.
SELECT Category, Date, ID
FROM table
WHERE (ID NOT IN('1','2','3') OR ID IS NULL)
AND Date = '01/06/2015'
Others have already shown how to fix this, so let me try to explain why this happens.
WHERE ID NOT IN('1','2','3')
is equivalent to
WHERE ID <> '1' AND ID <> '2' AND ID <> '3'
Since NULL <> anything yields UNKNOWN, your expression yields UNKNOWN and the record in question is not returned.
See the following Wikipedia article for details on this ternary logic:
Null (SQL): Comparisons with NULL and the three-valued logic (3VL)
Take a look at NULL comparison search conditions.
Use the IS NULL or IS NOT NULL clauses to test for a NULL value. This
can add complexity to the WHERE clause. For example, the TerritoryID
column in the AdventureWorks2008R2 Customer table allows null values.
If a SELECT statement is to test for null values in addition to
others, it must include an IS NULL clause:
SELECT CustomerID, AccountNumber, TerritoryID
FROM AdventureWorks2008R2.Sales.Customer
WHERE TerritoryID IN (1, 2, 3)
OR TerritoryID IS NULL
If you really want to be able to compare values to NULL's directly, you can do that as well. This is also described in the above article:
Transact-SQL supports an extension that allows for the comparison
operators to return TRUE or FALSE when comparing against null values.
This option is activated by setting ANSI_NULLS OFF.
Are you sure about you want ID fields as null?
Here is how you do it: (Assumins rest of your query is ok)
SELECT Category, Date, ID
FROM table
WHERE ID IS NULL
AND Date = '01/06/2015'
If you want records that does not have a category than you need to change your query as
SELECT Category, Date, ID
FROM table
WHERE Category IS NULL
AND Date = '01/06/2015'
You got a couple of options:
SELECT Category, Date, ID
FROM table
WHERE ISNULL(ID, '4') NOT IN('1','2','3')
AND Date = '01/06/2015'
Or what su8898 said
Please note that when you use "IN" or "NOT IN" which will not fetch any values if the column has got NULL values..
In your case, if you want to fetch only records with ID=NULL, then you can try the solution vgSefa suggested above..
If you want to pull all records with NULL as well as ID NOT IN('1','2','3'), then you could try something like this..
SELECT Category, Date, ID
FROM table
WHERE ID IS NULL
AND Date = '01/06/2015'
UNION ALL
SELECT Category, Date, ID
FROM table
WHERE ID NOT IN('1','2','3')
AND ID IS NOT NULL
AND Date = '01/06/2015'
Try this:
SELECT Category, Date, ID
FROM table
WHERE ID N
AND Date = '01/06/2015'

Single SQL Query to validate input values

I have written a Stored procedure which has three argumets
#UserID1 BIGINT
#UserID2 BIGINT
#UserID3 BIGINT
What I want to achieve is to write a single SQL query against table dbo.aspnet_UsersInRoles column ID so that #UserID1, #UserID2, #UserID3 are present in ID column of dbo.aspnet_UsersInRoles.
For example, I have received three values in variables and I want to confirm they are part of Id column. i.e. ID column of table has values 1,2,3,4,5,6,7,8,9,10 and UserID1 is 2, UserID2 is 5 and UserID3 is 7. So it should give true else false.
I can implement using three different queries but I am not getting any clue to do it in a single query.
You should be able to use something like this:
SELECT CASE WHEN SUM( CASE
WHEN #UserID1 = ID THEN 1
WHEN #UserID2 = ID THEN 1
WHEN #UserID3 = ID THEN 1
ELSE 0
END ) = 3 THEN 1 ELSE 0 AS [AllThree]
FROM aspnet_UsersInRoles
(untested code)
...assuming that the ID column is unique. If all three IDs are in the table, then you should end up with a summed value of 3. This will allow you to process the table in a single pass, but you don't get the advantages of index lookups like you get in Upendra's answer.

Sql query - getting rid of hard-coded values

I have the following query:
Select Name,
case when charindex('I',a.S_Data) > 0 then 1 else 0 end as Illustrated,
case when charindex('FP',a.S_Data) > 0 then 1 else 0 end as FrontPage,
case when charindex('BP',a.S_Data) > 0 then 1 else 0 end as BackPage,
case when charindex('ELP',a.S_Data) > 0 then 1 else 0 end as EDLP,
case when charindex('PR',a.S_Data) > 0 then 1 else 0 end as SpecialPromo
From Table1
What I would like to do is to store those filter values in some sort of lookup table or a settings table.
I am struggling with how to draw the values from a lookup table to use with this query.
I can think of at least two options...
CREATE TABLE constants (
id AS INT,
Illustrated AS VARCHAR(3),
FrontPage AS VARCHAR(3),
BackPage AS VARCHAR(3),
EDLP AS VARCHAR(3),
SpecialPromo AS VARCHAR(3)
)
INSERT INTO constants SELECT 1, 'I', 'FP', 'BP', 'ELP', 'PR'
SELECT
Name,
CASE WHEN CHARINDEX(constants.Illustrated, data.S_Data) > 0 THEN 1 ELSE 0 END AS Illustrated,
etc, etc
FROM
data
INNER JOIN
constants
ON constants.id = 1
Or...
CREATE TABLE constants (
constant_set_id AS INT,
constant_name AS VARCHAR(16),
value AS AS VARCHAR(3)
)
INSERT INTO constants SELECT 1, 'Illustrated', 'I'
INSERT INTO constants SELECT 1, 'FrontPage', 'FP'
INSERT INTO constants SELECT 1, 'BackPage', 'BP'
INSERT INTO constants SELECT 1, 'EDLP', 'ELP'
INSERT INTO constants SELECT 1, 'SpecialPromo', 'PR'
SELECT
Name,
MAX(CASE WHEN constants.constant_name = 'Illustrated' AND CHARINDEX(constants.value, data.S_Data) > 0 THEN 1 ELSE 0 END) AS Illustrated,
etc, etc
FROM
data
INNER JOIN
constants
ON constants.constant_set_id = 1
GROUP BY
data.name
Both let you have multiple different sets of constants. One is expandable without changing the schema, though the query still would need to change.
The main advantage of either approach is that you can re-use the constants else where, but store them once in a centralised location. Which is only relevant if/when the values in the constants needs updating. Re-use through indirection.
At the moment, your table apparently violates First Normal Form, since a single field can hold many values for a single record.
There are at least two ways this could be resolved:
(1) if the only values that can be stored in this field are the five specified in the query, it might make sense to replace the character field with five integer fields, each a flag for the specified condition - ie:
...
Illustrated int,
FrontPage int,
BackPage int,
EDLP int,
SpecialPromo int,
...
(2) If a variety of different conditions are to be stored, then I would suggest adding a lookup table for conditions, and a link table between the conditions and the original table - like so:
Conditions
----------
Condition_id
Description
Link_Table
----------
Table1_id
Condition_id
First, it would appear that Table1 is not first normal form (NFNF) because it violates the requirement that each tuple of has exactly one value for each attribute being of the type that is the declared type of that attribute i.e. the S_Data has multiple scalar types. You will suffer update anomalies e.g. deleting a setting presumably involves an UPDATE with text concatenation. Consider that SQL doesn't has operators that handle this kind of data (i.e. non-relational) very well.
Second, your output table is suboptimal becasue it returns the same type as multiple columns i.e. it looks more like a report.
Consider that the unit of work in SQL is the row:
CREATE TABLE Settings
(
Setting VARCHAR(15) NOT NULL UNIQUE
);
INSERT INTO Settings VALUES ('Illustrated'), ('FrontPage'), ('BackPage'),
('EDLP'), ('SpecialPromo');
CREATE TABLE Table1
(
Name VARCHAR(20) NOT NULL,
Setting VARCHAR(15) NOT NULL
REFERENCES Settings (Setting)
ON DELETE CASCADE
ON UPDATE CASCADE,
UNIQUE (Name, Setting)
);