Comparing fields in an ORACLE view - sql

I have an ORACLE table which includes the following fields:
FieldA1 NUMBER(10,0)
FieldA2 VARCHAR2(40 BYTE)
FieldB1 NUMBER(10,0)
FieldB2 VARCHAR2(40 BYTE)
How do I write an ORACLE view that reads from the table and if the following condition is true:
FieldA1 matches FieldB1 OR FieldA2 matches FieldB2
outputs the character 'Y' as one of the columns and if the condition above isn't true outputs 'N' ?

CREATE VIEW view_name
AS
SELECT fieldA1,
fieldA2,
fieldB1,
fieldB2,
(CASE WHEN fieldA1 = fieldB1 THEN 'Y'
WHEN fieldA2 = fieldB2 THEN 'Y'
ELSE 'N'
END) column_alias
FROM table_name

In addition to Justin Cave's answer:
Since the fields are not specified as NOT NULL, it may be convenient to interpret "fieldA match fieldB" as "fieldA = fieldB, or both fieldA and fieldB are nulls".
In this case replace fieldA = fieldB by
(fieldA = fieldB) or (fieldA is null and fieldB is null).

Related

Using Right() or Substr() in an UPDATE statement

I have two tables :
Add_T
Add_ID NUMBER(10,0)
Add_GEOMETRY SDO_GEOMETRY
STRTN VARCHAR2(40 BYTE)
CITY VARCHAR2(45 BYTE)
STATE VARCHAR2(2 BYTE)
ZIPCODE VARCHAR2(10 BYTE)
HC_ID NUMBER(10,0)
P_ID NUMBER(10,0)
Second Table :
HC_T
HC_GEOMETRY SDO_GEOMETRY
HC_ID NUMBER(10,0)
TYPE VARCHAR2(2 BYTE)
FACY NUMBER(10,0)
COORD_X NUMBER(15,0)
COORD_Y NUMBER(15,0)
I need to update the field HC_ID of Add_T Table and I use the following SQL statement:
update add_t set hc_Id = ( SELECT HC_ID FROM HC_T
WHERE ADD_T.P_ID = HC_T.FACY AND
HCO_T.TYPE='R' ) WHERE hc_ID IS NULL and
subtr(strtn,-4,4) = "-LOC"
It doesn't work. Also, I used the right() function and also got incorrect results.
update add_t set hc_Id = ( SELECT HC_ID FROM HC_T
WHERE ADD_T.P_ID = HC_T.FACY AND
HCO_T.TYPE='R' ) WHERE hc_ID IS NULL and
RIGHT(strtn,4) = "-LOC"
Could anyone say where is the mistake?
Looks like you need the last 4 characters of a string. If so, then you'd
UPDATE add_t
SET hc_id =
(SELECT hc_id
FROM hc_t
WHERE add_t.p_id = hc_t.facy
AND hco_t.TYPE = 'R')
WHERE hc_id IS NULL
AND SUBSTR (strtn, -4) = '-LOC';
because e.g.
SQL> SELECT SUBSTR ('some string', -4) FROM DUAL;
SUBS
----
ring
SQL>
(Note also that you'd enclose strings into single quotes in Oracle, not double ones (as you tried with the "-LOC" string).

ORACLE 12c Unique constraint failing in MERGE command which is matching all columns from the index

I have a table of aggregated data which were collected incorrectly and I am trying to merge results of select into it and I am hitting constraint violation and probably missing something obvious.
Table is simple:
CREATE TABLE "DVRA_STATS_AGG_HOURLY"
( "ID" NUMBER(*,0),
"SHIPMENTS" NUMBER,
"EVENT_DATETIME" DATE,
"COUNTRY" VARCHAR2(2 CHAR),
"DATA_TYPE" VARCHAR2(3 CHAR),
"EVENT_TYPE" CHAR(15)
)
CREATE UNIQUE INDEX "DVRA_STATS_AGG_HOURLY_PK" ON "DVRA_STATS_AGG_HOURLY" ("ID")
CREATE UNIQUE INDEX "DVRA_STATS_AGG_HOURLY_UK1" ON "DVRA_STATS_AGG_HOURLY" ("EVENT_DATETIME", "COUNTRY", "DATA_TYPE", "EVENT_TYPE")
CREATE INDEX "DVRA_STATS_AGG_HOURLY_INDEX1" ON "DVRA_STATS_AGG_HOURLY" ("EVENT_DATETIME" DESC)
With trigger sequence and trigger
CREATE OR REPLACE EDITIONABLE TRIGGER "DVRA_STATS_AGG_HOURLY_AINC"
BEFORE INSERT ON dvra_stats_agg_hourly
FOR EACH ROW
BEGIN
SELECT stats_seq.NEXTVAL
INTO :new.id
FROM dual;
END;
Merge is not complicated either:
MERGE INTO dvra_stats_agg_hourly stats
USING(
SELECT COUNT(*) as SHIPMENTS, TRUNC(event_datetime,'HH24') as event_datetime,COUNTRY,data_type,event_type
FROM AUDIT
WHERE event_type in (<List of events>)
and TRUNC(event_datetime,'HH24') < trunc(sysdate,'HH24')
and audittable.event_datetime is not null
and audittable.COUNTRY is not null
and audittable.data_type is not null
and audittable.event_type is not null
GROUP BY TRUNC(event_datetime,'HH24'),COUNTRY,data_type,event_type
) audittable
ON (
audittable.event_datetime = stats.event_datetime
and audittable.COUNTRY = stats.COUNTRY
and audittable.data_type = stats.data_type
and audittable.event_type = stats.event_type
)
WHEN MATCHED THEN
UPDATE SET stats.SHIPMENTS = audittable.SHIPMENTS WHERE stats.SHIPMENTS <> audittable.SHIPMENTS
WHEN NOT MATCHED THEN
INSERT (SHIPMENTS,event_datetime,STATS.COUNTRY,data_type,event_type)
VALUES (audittable.SHIPMENTS,audittable.event_datetime,audittable.COUNTRY ,audittable.data_type ,audittable.event_type)
;
And I am getting error:
ORA-00001: unique constraint (DVRA_MONITORING.DVRA_STATS_AGG_HOURLY_UK1) violated
I am matching on same columns as I am using in unique constraint. I am inserting only not matching rows and I am grouping by same columns so there should be only one row for unique combination of those columns.
I am only one updating table when I am doing this operation, so no other session manipulating data during run of the query.
What am I missing here?
Edit: Data in DVRA_STATS_AGG_HOURLY were inserted with insert into select. With same select which is used in this merge. Just some data were not properly loaded yet, so I am making correction.
Edit2: added not null
Edit3: Changes in merge query after discussion with Alex
MERGE INTO dvra_stats_agg_hourly stats
USING(
SELECT COUNT(*) as SHIPMENTS, TRUNC(event_datetime,'HH24') as event_datetime,COUNTRY,data_type,event_type
FROM BCTCUSTOM.V_DVRA_AUDIT
WHERE event_type in (<List of types>)
and TRUNC(event_datetime,'HH24') < trunc(sysdate,'HH24')
and TRUNC(event_datetime,'HH24') is not null
and event_datetime is not null
and COUNTRY is not null
and data_type is not null
and event_type is not null
GROUP BY TRUNC(event_datetime,'HH24'),COUNTRY,data_type,event_type
) audittable
ON (
audittable.event_datetime = stats.event_datetime
and ((audittable.COUNTRY is null and stats.COUNTRY is null) or audittable.COUNTRY = stats.COUNTRY)
and ((audittable.data_type is null and stats.data_type is null) or audittable.data_type = stats.data_type)
and ((audittable.event_type is null and stats.event_type is null) or audittable.event_type = stats.event_type)
)
WHEN MATCHED THEN
UPDATE SET stats.SHIPMENTS = audittable.SHIPMENTS WHERE stats.SHIPMENTS <> audittable.SHIPMENTS
WHEN NOT MATCHED THEN
INSERT (SHIPMENTS,event_datetime,STATS.COUNTRY,data_type,event_type)
VALUES (audittable.SHIPMENTS,audittable.event_datetime,audittable.COUNTRY ,audittable.data_type ,audittable.event_type)
;
If any of the four columns you are comparing in your ON clause are null then they won't match with your current conditions, since null = null is unknown.
You can add explicit null checks; instead of:
ON (
audittable.event_datetime = stats.event_datetime
and audittable.COUNTRY = stats.COUNTRY
and audittable.data_type = stats.data_type
and audittable.event_type = stats.event_type
)
do something like:
ON (
audittable.event_datetime = stats.event_datetime
and ((audittable.COUNTRY is null and stats.COUNTRY is null)
or audittable.COUNTRY = stats.COUNTRY)
and ((audittable.data_type is null and stats.data_type is null)
or audittable.data_type = stats.data_type)
and ((audittable.event_type is null and stats.event_type is null)
or audittable.event_type = stats.event_type)
)
db<>fiddle without nulls - works OK.
db<>fiddle with nulls - initially fails, but works with explicit null checks added.
Another possibility is a data type mismatch, as you're using CHAR(15) in the table you showed. If the audit table has that column defined as VARCHAR2(15) instead then the comparison will also fail (because "Oracle uses nonpadded comparison semantics whenever one or both values in the comparison have the data type VARCHAR2 or NVARCHAR2"), and the implicit conversion during the insert will cause the constraint violation. In that case, you can trim the CHAR value:
ON (
audittable.event_datetime = stats.event_datetime
and audittable.COUNTRY = stats.COUNTRY
and audittable.data_type = stats.data_type
and audittable.event_type = trim(stats.event_type)
)
db<>fiddle

PostgreSQL insert and ignore multiple columns

I have newly created table with 4 unique fields: nsn, slug, part_no, mfg_sku.
I'm trying to insert into this table approx. 2 million rows from multiple tables, which has also repeating rows.
so the main question is...
how to emulate insert ignore in this case?
Select distinct based on multiple columns or what do you suggest?
Here is the data that I want to insert to Part table
SELECT "PartOld".id,
"PartOld"."PartNo",
"PartOld"."Manufacturer",
"PartOld"."Slug",
"PartOld"."Description",
"PartOld"."NSN",
NULL::numeric AS price,
"PartOld".name,
NULL::character varying AS mfg_sku
FROM "PartOld"
UNION
SELECT part_item.id,
NULL::character varying AS "PartNo",
part_item.manufacturer AS "Manufacturer",
NULL::character varying AS "Slug",
part_item.details AS "Description",
part_item.msn AS "NSN",
NULL::numeric AS price,
part_item.name,
part_item.mfg_sku
FROM part_item_fetched part_item
And Here is the Part table.
DROP TABLE IF EXISTS "electronic_parts"."Part";
CREATE TABLE "electronic_parts"."Part" (
"id" int4 NOT NULL DEFAULT nextval('"Part_id_seq"'::regclass),
"part_no" varchar COLLATE "default",
"manufacturer" varchar COLLATE "default",
"description" text COLLATE "default",
"slug" varchar COLLATE "default",
"nsn" varchar COLLATE "default",
"price" numeric,
"name" varchar COLLATE "default",
"mfg_sku" varchar COLLATE "default"
)
WITH (OIDS=FALSE);
ALTER TABLE "electronic_parts"."Part" OWNER TO "root";
-- ----------------------------
-- Primary key structure for table Part
-- ----------------------------
ALTER TABLE "electronic_parts"."Part" ADD PRIMARY KEY ("id") NOT DEFERRABLE INITIALLY IMMEDIATE;
-- ----------------------------
-- Indexes structure for table Part
-- ----------------------------
CREATE UNIQUE INDEX "part_u1" ON "electronic_parts"."Part" USING btree(part_no COLLATE "default" "pg_catalog"."text_ops" ASC NULLS LAST);
CREATE UNIQUE INDEX "part_u2" ON "electronic_parts"."Part" USING btree(nsn COLLATE "default" "pg_catalog"."text_ops" ASC NULLS LAST);
CREATE UNIQUE INDEX "part_u3" ON "electronic_parts"."Part" USING btree(mfg_sku COLLATE "default" "pg_catalog"."text_ops" ASC NULLS LAST);
Use
INSERT INTO ...
(SELECT ...)
ON CONFLICT DO NOTHING;
This has been available since 9.5, see the documentation.
You can filter your input with anti joins (tipically with NOT EXISTS() or LEFT JOIN + IS NULL):
WITH data AS (
-- your query here
SELECT ...
UNION ALL ...
),
conflicting_ids AS (
SELECT id FROM data GROUP BY id HAVING COUNT(*) > 1
),
conflicting_part_nos AS (
SELECT part_no FROM data GROUP BY part_no HAVING COUNT(*) > 1
),
conflicting_nsns AS (
SELECT nsn FROM data GROUP BY nsn HAVING COUNT(*) > 1
),
conflicting_mfg_skus AS (
SELECT mfg_sku FROM data GROUP BY mfg_sku HAVING COUNT(*) > 1
)
INSERT INTO table_name (column_names)
SELECT d.*
FROM data d
WHERE NOT EXISTS(SELECT 1 FROM conflicting_ids a WHERE a.id = d.id)
AND NOT EXISTS(SELECT 1 FROM conflicting_part_nos a WHERE a.part_no = d.part_no)
AND NOT EXISTS(SELECT 1 FROM conflicting_nsns a WHERE a.nsn = d.nsn)
AND NOT EXISTS(SELECT 1 FROM conflicting_mfg_skus a WHERE a.mfg_sku = d.mfg_sku)
ON CONFLICT DO NOTHING
Notes: id is unique too, because it is the primary key. Also, because the conflicting rows are checked separately, you won't need UNION. You can use UNION ALL, which will be a slightly more efficient.

Inserting a Date data

so I have this table
RPG_RETCON (
UNIQUE_ID VARCHAR2(100 BYTE),
CONTAINER VARCHAR2(100 BYTE),
DATA_POINT_NAME VARCHAR2(100 BYTE),
SOURCE_VALUE VARCHAR2(100 BYTE),
CSS_VALUE VARCHAR2(100 BYTE),
STATUS VARCHAR2(100 BYTE)
)
And I I'm trying to insert this select statement into that table.
INSERT INTO RPG_RETCON
(SELECT A.POOL_CUSIP_ID AS UNIQUE_ID,
'1_13_1C' AS CONTAINER,
'SECU_ACTL_STLM_DT' AS COLUMN_NAME1,
TO_CHAR(A.SECU_ACTL_STLM_DT),
TO_CHAR(B.SECU_ACTL_STLM_DT),
CASE
WHEN A.SECU_ACTL_STLM_DT = B.SECU_ACTL_STLM_DT
THEN
'PASS'
ELSE
'FAIL'
END
AS STATUS
FROM POOL_1_13_1C_TRGT A
LEFT JOIN POOL_1_13_1C_CSS B ON A.POOL_CUSIP_ID = B.POOL_CUSIP_ID);
Now the problem is that SECU_ACTL_STLM_DT is a date field and when I try to do the inserts, I get an invalid number error. If I take away the TO_CHAR to just A.SECU_ACTL_STLM_DT,
B.SECU_ACTL_STLM_DT,
I get invalid month.
Note: I absolutely cannot change
SOURCE_VALUE VARCHAR2(100 BYTE)
CSS_VALUE VARCHAR2(100 BYTE)
-- Within the table structure...
They need to be VARCHAR2 Data types.
Are there any suggestions to where I can insert this select statement error free?
I think your code should work. However, I would list the columns explicitly and add date formats for the insert. Perhaps this will help:
INSERT INTO RPG_RETCON(UNIQUE_ID, CONTAINER, COLUMN_NAME1, SOURCE_VALUE, CSS_VALUE, STATUS)
SELECT A.POOL_CUSIP_ID AS UNIQUE_ID, '1_13_1C' AS CONTAINER,
'SECU_ACTL_STLM_DT' AS COLUMN_NAME1,
TO_CHAR(A.SECU_ACTL_STLM_DT, 'YYYY-MM-DD'),
TO_CHAR(B.SECU_ACTL_STLM_DT, 'YYYY-MM-DD'),
(CASE WHEN A.SECU_ACTL_STLM_DT = B.SECU_ACTL_STLM_DT
THEN 'PASS'
ELSE 'FAIL'
END) AS STATUS
FROM POOL_1_13_1C_TRGT A LEFT JOIN
POOL_1_13_1C_CSS B
ON A.POOL_CUSIP_ID = B.POOL_CUSIP_ID;
It is possible that one of the SECU_ACTL_STLM_DT columns is not a date and the comparison is failing.

Creating table in Oracle

My requirement is that the column accno has no null value and no duplicates. The name column has no nulls and accepts only A to Z (no other like number or * $). The acctype columns is a character that allows only ( 'S' , 'C' ,'R') and the balance column has no null values. If acctype is S then balance should be >= 5000, when C the balance should be > 10000 and when it's R >= 5000.
I am trying to apply this with:
create table kcb_acc_tab
(accno varchar2(20)
constraint kcb_acc_Pk
primary key,
name varchar2(20)
constraint kcb_name_NN
Not null
constraint kcb_name_CK
check((name =upper(name)) and (name like '[(A-Z)]')),
Acctype char
constraint kcb_acctype_ck
check (acctype in('S' ,'C' ,'R')) ,
Doo timestamp
default sysdate ,
bal number(7,2) kcb_bal_NN
constraint kcb_bal_ck
check((aacctype ='S' and bal >=5000) or
(acctype = 'C' and bal >=10000) or
(acctype ='R' and bal >=5000));
This sounds like a perfect use-case for regular expressions, which I think is your intention with the like in your constraint.
I've considerably cleaned up your statement, you were missing a comma in the definition of kcb_bal_ck, and I've put the constraints at the end and added whitespace. This makes it easier, for me, to see what's going on and where any mistakes might be.
create table kcb_acc_tab(
accno varchar2(20) not null
, name varchar2(20) not null
, acctype char(1) not null -- missing size of column
, doo timestamp default sysdate not null -- missing not null
, bal number(7,2) not null
, constraint kcb_acc_pk primary key (accno)
, constraint kcb_name_ck check ( regexp_like(name, '[A-Z]', 'c' ) )
, constraint kcb_acctype_ck check ( acctype in ( 'S' ,'C' ,'R' ) )
-- acctype was spelled incorrectly.
, constraint kcb_bal_ck check( ( acctype ='S' and bal >= 5000 )
or ( acctype = 'C' and bal >= 10000 )
or ( acctype ='R' and bal >= 5000 )
) -- this parenthesis was missing
)
Here's a SQL Fiddle to demonstrate.
The main difference between this and your own is regexp_like(name, '[A-Z]', 'c' ). This ensures that the characters in the column name are solely contained within the group A-Z, that is the set of the upper-case Latin alphabet. The match_parameter 'c', specifies that the matching should be case-sensitive. The default case-sensitivity is determined by your NLS_SORT parameter, so you may not need to specify this explicitly but it's wise to do so.