Decode returning multiple rows - decode

I have a business case where in a table I may have a specific value or it will have value as 'ALL'. While I query the table if particular input matches I need that particular record. If its not matching then entry with ALL should be considered and returned.
Here is the case that you have try at your end to replicate the issues.
CREATE TABLE lookup_value (
country VARCHAR2(2) NOT NULL,
department VARCHAR2(30) NOT NULL,
problem_code VARCHAR2(30),
disposition VARCHAR2(30));
INSERT INTO lookup_value VALUES ('US','SALES','P0044','EXCHANGE')
INSERT INTO lookup_value VALUES ('US','SALES','ALL','EXCHANGE')
INSERT INTO lookup_value VALUES ('US','SALES','P0020','ALL')
INSERT INTO lookup_value VALUES ('US','SALES','ALL','ALL')
INSERT INTO lookup_value VALUES ('US','OPERATIONS','P0044','ACKNOELEDGE')
INSERT INTO lookup_value VALUES ('US','OPERATIONS','ALL', 'ALL')
SELECT *
FROM lookup_value
WHERE country = 'US'
AND department = 'SALES'
AND problem_code = DECODE (:problem, problem_code, :problem, 'ALL')
AND disposition = DECODE (:disp, disposition, :disp, 'ALL')
If I run the above query passing problem_code = P0044 and disposition = 'EXCHANGE', it returns me 3 rows, where as I need only one.
COUNTRY DEPARTMENT PROBLEM_CODE DISPOSITION
-----------------------------------------------------------
US SALES P0044 EXCHANGE
US SALES ALL EXCHANGE
US SALES ALL ALL
Regards
Tauseef

Related

How to subtract two rows from two different selects?

I'm trying to subtract the total number shares for each symbol from the sell orders and the buy orders so I can have that total of shares owned.
buy = db.execute("SELECT symbol,SUM(shares) as total FROM negocios WHERE userid = ? and operation = 'buy' GROUP BY symbol");
sell = db.execute("SELECT symbol,SUM(shares) as total FROM negocios WHERE userid = ? and operation = 'sell' GROUP BY symbol")
I want a table like this with the values already subtracted:
symbol
total
CSCO
25
GOOG
6
NFLX
32
I was trying :
SELECT ((SELECT symbol,SUM(shares) as total FROM negocios WHERE userid = ? and operation = 'buy' GROUP BY symbol) - (SELECT symbol,SUM(shares) as total FROM negocios WHERE userid = ? and operation = 'sell' GROUP BY symbol) AS diff;
But I'm getting an error:
Parse error: sub-select returns 2 columns - expected 1
Table schema:
CREATE TABLE negocios (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, userid TEXT NOT NULL, symbol TEXT NOT NULL, operation TEXT CHECK( operation IN ('buy', 'sell')), price DECIMAL(10,2), time TIMESTAMP, shares DECIMAL(10,2), name TEXT);
You can do it with a single query if you use conditional aggregation:
userid = 100 #variable that stores the user's id
sql = """
SELECT symbol,
SUM(shares * CASE operation WHEN 'buy' THEN 1 WHEN 'sell' THEN -1 END) AS total
FROM negocios
WHERE userid = ?
GROUP BY symbol
"""
result = db.execute(sql, (userid,))

How to insert data from R into Oracle table with identity column?

Assume I have a simple table in Oracle db
CREATE TABLE schema.d_test
(
id_record integer GENERATED AS IDENTITY START WITH 95000 NOT NULL,
DT DATE NOT NULL,
var varchar(50),
num float,
PRIMARY KEY (ID_RECORD)
)
And I have a dataframe in R
dt = c('2022-01-01', '2005-04-01', '2011-10-02')
var = c('sgdsg', 'hjhgjg', 'rurtur')
num = c(165, 1658.5, 8978.12354)
data = data.frame(dt, var, num)%>%
mutate(dt = as.Date(dt))
I'm trying to insert data into Oracle d_test table using the code
data %>%
dbWriteTable(
oracle_con,
value = .,
date = T,
'D_TEST',
append = T,
row.names=F,
overwrite = F
)
But the following error returned
Error in .oci.WriteTable(conn, name, value, row.names = row.names, overwrite = overwrite, :
Error in .oci.GetQuery(con, stmt, data = value) :
ORA-00947: not enough values
What's the problem?
How can I fix it?
Thank you.
This is pure Oracle (I don't know R).
Sample table:
SQL> create table test_so (id number generated always as identity not null, name varchar2(20));
Table created.
SQL> insert into test_so(name) values ('Name 1');
1 row created.
My initial idea was to suggest you to insert any value into the ID column, hoping that Oracle would discard it and generate its own value. However, that won't work.
SQL> insert into test_so (id, name) values (-100, 'Name 2');
insert into test_so (id, name) values (-100, 'Name 2')
*
ERROR at line 1:
ORA-32795: cannot insert into a generated always identity column
But, if you can afford recreating the table so that it doesn't automatically generate the ID column's value but use a "workaround" (we used anyway, as identity columns are relatively new in Oracle) - a sequence and a trigger - you might be able to "fix" it.
SQL> drop table test_so;
Table dropped.
SQL> create table test_so (id number not null, name varchar2(20));
Table created.
SQL> create sequence seq_so;
Sequence created.
SQL> create or replace trigger trg_bi_so
2 before insert on test_so
3 for each row
4 begin
5 :new.id := seq_so.nextval;
6 end;
7 /
Trigger created.
Inserting only name (Oracle will use a trigger to populate ID):
SQL> insert into test_so(name) values ('Name 1');
1 row created.
This is what you'll do in your code - provide dummy ID value, just to avoid
ORA-00947: not enough values
error you have now. Trigger will discard it and use sequence anyway:
SQL> insert into test_so (id, name) values (-100, 'Name 2');
1 row created.
SQL> select * from test_so;
ID NAME
---------- --------------------
1 Name 1
2 Name 2 --> this is a row which was supposed to have ID = -100
SQL>
The way you can handle this problem is to create table with GENERATED BY DEFAULT ON NULL AS IDENTITY like this
CREATE TABLE CM_RISK.d_test
(
id_record integer GENERATED BY DEFAULT ON NULL AS IDENTITY START WITH 5000 NOT NULL ,
DT date NOT NULL,
var varchar(50),
num float,
PRIMARY KEY (ID_RECORD)
)

Updating a singular row in a trigger SQLite

Table rental has values (ID, odo_out, date),
table vehicle has values (ID, odo, car),
both with more columns but not relevant to this.
I have attempted to create a trigger:
CREATE TRIGGER odo_update AFTER INSERT ON rental
BEGIN
UPDATE rental SET odo_out = (SELECT Vehicle.odo FROM Vehicle WHERE rental.ID = Vehicle.ID)
WHERE EXISTS (SELECT * FROM Vehicle WHERE Vehicle.ID = rental.ID);
END;
which should detect a NULL for rental.odo_out and replace it with the value in Vehicle.odo for corresponding ID. This does work, but it updates every row in table, whereas I want it to ONLY update the row with NULL, ie the new row being inserted. An ID can be repeated multiple times in the rental table. How can I do this?
You must set the condition so that only the new row is updated.
This is where you need the keyword NEW to refer to the columns of the new row:
CREATE TRIGGER odo_update AFTER INSERT ON rental
WHEN NEW.odo_out IS NULL
BEGIN
UPDATE rental
SET odo_out = (SELECT odo FROM Vehicle WHERE ID = NEW.ID)
WHERE ID = NEW.ID;
END;

substring: row information to field names

I have information in an SQLite database. The database structure can not be changed.
I am trying to construct a query that will give me a result in which the TypeOfInformation entries are field names:
My first try was to work with subqueries:
SELECT (SELECT Value FROM FinData WHERE Type = 'Price') AS Price,
(SELECT Value FROM FinData WHERE Type = 'Volume') AS Volume
FROM FinData")
Seemed perfect, however, the result was a resultset in which EVERY entry in the columns Price and Volume are equal to the FIRST respective entry of Price and Volume in the original database:
I tried to get around this and to include the other Price and Volume information ­- but I failed. (Which is a pity, because the syntax seemed somehow easy to grasp.)
Next try was the following:
Select Date, Value AS Volume From FinData WHERE Volume IN
(SELECT Value FROM FinData WHERE (Type = 'Volume'))
This gives me a resultset with a Volume column and all volume information. Okay, so far. However, when I want to complement this resultset which a Price column via
Select Date, Value AS Volume From FinData WHERE Volume IN
(SELECT Value FROM FinData WHERE (Type = 'Volume'))
union
Select Date, Value AS Close From FinData WHERE Price IN
(SELECT Value FROM FinData WHERE (Type = 'Price'))
I get a resultset that shows Price and Volume information in only ONE column ("Volume"), which therefore is also useless.
To look up a value corresponding to a row in the outer query, you have to use a correlated subquery, which explicitly makes a connection between both:
SELECT Date,
(SELECT Value
FROM FinData
WHERE Date = Dates.Date
AND TypeOfInformation = 'Price'
) AS Price,
(SELECT Value
FROM FinData
WHERE Date = Dates.Date
AND TypeOfInformation = 'Volume'
) AS Volume
FROM (SELECT DISTINCT Date
FROM FinData) AS Dates;
(The DISTINCT subquery is used to prevent multiple rows for each date.)
Alternatively, group all rows for a date, and use aggregation functions and CASE expressions to extract the values from the proper rows:
SELECT Date,
MAX(CASE WHEN TypeOfInformation = 'Price' THEN Value END) AS Price,
MAX(CASE WHEN TypeOfInformation = 'Volume' THEN Value END) AS Volume
FROM FinData
GROUP BY Date;
Assuming dates are unique per price volume pair, you can do this:
with xxx(date,price,volume) as
(
select date,value,0 from findata where typeofinformation = 'Price'
union
select date,0,value from findata where typeofinformation = 'Volume'
)
select date,sum(price) price,sum(volume) volume from xxx group by date;

How to increment quantity column if record exists, else insert record?

I've got the below sqlite database:
CREATE TABLE `test` (
`idx` INTEGER PRIMARY KEY AUTOINCREMENT,
`qty` INTEGER,
`brand` TEXT,
`type` TEXT
);
insert into test (qty, brand, type) values (1, "Merc", "petrol");
insert into test (qty, brand, type) values (1, "Toyota", "diesel");
I want to insert another record but if it exists then I want to increase the qty field, so:
insert into test (qty, brand, type) values (1, "Merc", "diesel");
so would just increase the qty field by one, and
insert into test (qty, brand, type) values (1, "Merc", "petrol");
would insert a new record.
SQLite is an embedded database, i.e., it is designed to be used together with an application.
The easiest way is to put the logic into the application:
db.execute("UPDATE test SET qty = qty + 1 WHERE brand = ? AND type = ?",
["Merc", "diesel"])
if db.rows_affected == 0:
db.execute("INSERT ...")
As of version 3.24.0, SQLite supports UPSERT.
CREATE TABLE `test` (
`idx` INTEGER PRIMARY KEY AUTOINCREMENT,
`qty` INTEGER DEFAULT 1,
`brand` TEXT,
`type` TEXT
)
UNIQUE (brand, type)
;
INSERT INTO test (qty, brand, type) VALUES ("Merc", "diesel")
ON CONFLICT(brand, type) DO UPDATE SET qty = qty + 1;
INSERT INTO test (qty, brand, type) VALUES ("Merc", "diesel")
ON CONFLICT(brand, type) DO UPDATE SET qty = qty + 1;
SELECT qty, brand, type FROM test;
Output:
2 Merc diesel

Resources