How can I properly handle an sqlite table self reference? - sqlite

I have created an sqlite db for a small cattle herd.
CREATE TABLE Animals
(
animal_id PRIMARY KEY,
animal_name CHAR(15) NULL,
date_born DATE NULL,
f_parent REFERENCES Animals (animal_id) NULL,
m_parent REFERENCES Animals (animal_id) NULL,
date_purchased DATE NULL,
registered BIT NOT NULL,
gender CHAR(1) NOT NULL CHECK(gender IN ("M","F")),
breed INTEGER NOT NULL REFERENCES breed (breed_id)
);
CREATE TABLE termination (term_key INTEGER PRIMARY KEY, animal_id INTEGER, term_date DATE, sold BIT, price SMALLMONEY, comp_market_price SMALLMONEY, comp_market_tier TEXT);
I have this statement:
SELECT a1.animal_id, a1.animal_name, a1.f_parent, t1.term_date, t1.price, t1.comp_market_price, t1.comp_market_tier
FROM Animals AS a1, termination AS t1
WHERE a1.animal_id = t1.animal_id
AND a1.f_parent NOT NULL;
results are:
id#|'animal name'|'parent id#'|date sold ...
15|some name|4|2014-05-26 ...
...
which is correct and what I wanted except that in place of 'parent id#' I want the parent's name. The parent id# is a key in the same table as the offspring (as you see from my create statement above), but I can't figure out how to deal with this self-reference. I know the issue is rather common, and I've tried view tables, multiple joins, etc. to no avail. Please show code snippet of how I can print the same results showing the parents name in place of parent id#/key no.
thank you very much!

Something like this maybe?
select a.animal_id, a.animal_name,
(select animal_name
from animals
where a.f_parent = animal_id) as parent,
t.term_date, t.price, t.comp_market_price, t.comp_market_tier
from animals as a, termination as t using(animal_id)
where a.f_parent not null;
or this? (better execution plan)
select a.animal_id as id, a.animal_name as name,f.animal_name as mother,
t.term_date, t.price, t.comp_market_price, t.comp_market_tier
from animals as a, termination as t using(animal_id),
animals f
where a.f_parent = f.animal_id
and a.f_parent is not null;

Related

SQLite: trigger to check newly inserted row against limits in another table

I have a database with the following schema:
Sensors (
ID INTEGER PRIMARY KEY AUTOINCREMENT
UNIQUE
NOT NULL,
SITE_ID INTEGER REFERENCES Sites (ID)
NOT NULL,
NAME TEXT UNIQUE
NOT NULL
);
Sites (
ID INTEGER PRIMARY KEY AUTOINCREMENT
UNIQUE
NOT NULL,
NAME TEXT UNIQUE
NOT NULL
);
Data (
SENSOR_ID INTEGER REFERENCES Sensors (ID)
NOT NULL,
COUNT INTEGER NOT NULL,
TIME DATETIME NOT NULL,
TYPE TEXT NOT NULL,
VALUE REAL NOT NULL,
PRIMARY KEY (
SENSOR_ID,
COUNT,
TIME,
TYPE
)
);
Limits (
SITE_ID INTEGER REFERENCES Sites (ID)
NOT NULL,
TYPE TEXT NOT NULL,
HIGH REAL NOT NULL,
LOW REAL NOT NULL,
PRIMARY KEY (
SITE_ID,
TYPE
)
);
Data contains all the data for all sensors and sites, Data has a few different types. The limits have a row for each type of data for each of the sites. I want to create a table identical to Data (called Warnings) containing only the rows where the value lies outside of the limits in Limit.
I want to do this with a trigger because the limits can change and the rows are added to data sporadically.
Here's my best attempt so far:
CREATE TRIGGER VALUE_OUT_OF_RANGE
AFTER INSERT
ON Data
WHEN NEW.VALUE < (SELECT Limits.LOW FROM Limits INNER JOIN Sensors ON (Sensors.SITE_ID = Limits.SITE_ID) WHERE (Sensors.ID = NEW.SENSOR_ID AND Limits.TYPE = NEW.TYPE))
BEGIN
INSERT INTO Warnings(SENSOR_ID, COUNT, TIME, TYPE, VALUE) VALUES(NEW.SENSOR_ID, NEW.COUNT, NEW.TIME, NEW.TYPE, NEW.VALUE);
END;
In my attempt I've only checked against the lower limit, in the final trigger I would like to check that the value is between the LOW and HIGH limits.
I've tried the Select statement seperately and I get the data that I am looking for.
Any help or suggestion is greatly appreciated.
I managed to find a solution that works.
Posting it in case it helps anyone.
CREATE TRIGGER VALUE_OUT_OF_RANGE
AFTER INSERT
ON Data
FOR EACH ROW
WHEN NEW.VALUE < (
SELECT Limits.LOW
FROM Limits
JOIN
Sensors ON (Sensors.SITE_ID = Limits.SITE_ID)
WHERE (Sensors.ID = NEW.Sensor_ID AND
Limits.TYPE = NEW.TYPE)
LIMIT 1
)
OR
NEW.VALUE > (
SELECT Limits.HIGH
FROM Limits
JOIN
Sensors ON (Sensors.SITE_ID = Limits.SITE_ID)
WHERE (Sensors.ID = NEW.Sensor_ID AND
Limits.TYPE = NEW.TYPE)
LIMIT 1
)
BEGIN
INSERT INTO Warnings (
SENSOR_ID,
COUNT,
TIME,
TYPE,
VALUE
)
VALUES (
NEW.SENSOR_ID,
NEW.COUNT,
NEW.TIME,
NEW.TYPE,
NEW.VALUE
);
END;

PLSQL query union sum query

I need to output running balance in the following query, Please help me with the query-code:
select
WEAVING_YARN_TR.TR_ID,
WEAVING_YARN_TR.GP_NO,
WEAVING_YARN_TR.YR_ID,
WEAVING_YARN_GATEPASS.TO_FROM as PARTY,
WEAVING_YARN_GATEPASS.GP_DATE,
WEAVING_YARN_TR.BAGS_IN,
WEAVING_YARN_TR.BAGS_OUT,
SUM(NVL(WEAVING_YARN_TR.BAGS_OUT,0)-NVL(WEAVING_YARN_TR.BAGS_IN,0)) over (order by GP_DATE) as BALANCE
from WEAVING_YARN_TR,WEAVING_YARN_GATEPASS
where WEAVING_YARN_TR.GP_NO=WEAVING_YARN_GATEPASS."GP_NO." and WEAVING_YARN_TR.YR_ID=:P216_YR_ID and WEAVING_YARN_GATEPASS.TO_FROM=:P216_YR_DESC
UNION ALL
select
null as TR_ID,
WEAVING_WARP_SET.SET_ID,
WEAVING_WARP_SET.YARN_ITEM,
WEAVING_WARP_SET.PARTY,
WEAVING_WARP_SET.RECEIVED_DATE,
WEAVING_WARP_SET.TOTAL_BAGS_CONSUMED,
null as BAGS_OUT,
NVL(null,0)-NVL(WEAVING_WARP_SET.TOTAL_BAGS_CONSUMED,0) as BALANCE
from WEAVING_WARP_SET
where WEAVING_WARP_SET.YARN_ITEM=:P216_YR_ID and WEAVING_WARP_SET.PARTY= :P216_YR_DESC
order by GP_DATE
So here is the thing; if you're going to use UNION or UNION ALL, each query block has to be the equal column and type.
For example; We have 2 queries and we want to bind them.
The first query block contains 3 columns and their type NUMBER, VARCHAR, NUMBER (in order). The second block has to be 3 column and types must be NUMBER, VARCHAR, VARCHAR (in order)
So your query does not seem like that. You want an output like the screenshot that you sent. Here is the query that edited:
select
weaving_yarn_gatepass.gp_date
,weaving_yarn_tr.gp_no
,weaving_yarn_gatepass.to_from as party
,weaving_yarn_tr.yr_id
,weaving_yarn_tr.bags_in
,weaving_yarn_tr.bags_out
,sum(nvl(weaving_yarn_tr.bags_out,0) - nvl(weaving_yarn_tr.bags_in,0)) over(order by gp_date) as balance
from weaving_yarn_tr
,weaving_yarn_gatepass
where weaving_yarn_tr.gp_no = WEAVING_YARN_GATEPASS."GP_NO."
and weaving_yarn_tr.yr_id = :p216_yr_id
and weaving_yarn_gatepass.to_from = :p216_yr_desc
union all
select
weaving_warp_set.received_date --I don't know if it means gp_date
,null as gp_no
,weaving_warp_set.party
,null yr_id -- If there is a column named YR_ID in WEAVING_WARP_SET put here
,weaving_warp_set.total_bags_consumed
,null as bags_out
,nvl(null,0) - nvl(weaving_warp_set.total_bags_consumed,0) as balance
from weaving_warp_set
where weaving_warp_set.yarn_item = :p216_yr_id
and weaving_warp_set.party = :p216_yr_desc
order by gp_date;

Cannot replace a string with several random strings taken from another table in sqlite

I'm trying to replace a placeholder string inside a selection of 10 random records with a random string (a name) taken from another table, using only sqlite statements.
i've done a subquery in order to replace() of the placeholder with the results of a subquery. I thought that each subquery loaded a random name from the names table, but i've found that it's not the case and each placeholder is replaced with the same string.
select id, (replace (snippet, "%NAME%", (select
name from names
where gender = "male"
) )
) as snippet
from imagedata
where timestamp is not NULL
order by random()
limit 10
I was expecting for each row of the SELECT to have different random replacement every time the subquery is invoked.
hello i'm %NAME% and this is my house
This is the car of %NAME%, let me know what you think
instead each row has the same kind of replacement:
hello i'm david and this is my house
This is the car of david, let me know what you think
and so on...
I'm not sure it can be done inside sqlite or if i have to do it in php over two different database queries.
Thanks in advance!
Seems that random() in the subquery is only evaluated once.
Try this:
select
i.id,
replace(i.snippet, '%NAME%', n.name) snippet
from (
select
id,
snippet,
abs(random()) % (select count(*) from names where gender = 'male') + 1 num
from imagedata
where timestamp is not NULL
order by random() limit 10
) i inner join (
select
n.name,
(select count(*) from names where name < n.name and gender = 'male') + 1 num
from names n
where gender = 'male'
) n on n.num = i.num

How to get value from oracle stored in || separated?

I am new in oracle and I want to get the value from a column which is stored as "Ashu||123 ||Main Menu|ENG||1|1".
as you can see each value is separated by || symbol.in the above value Ashu is the customer name and 123 is the id, I want both value as customer-name and customer id.
In the query below, I include some test data "on the fly" (not part of the solution; use your actual table name instead of test_data in the main query, and your actual column name instead of str). I included several special cases for testing, to make sure the query works correctly in all cases. I assume the first value (before the first ||) is the customer name and the second the customer id, and the rest of the input string can be ignored. I looked in particular to see that the query handles null values correctly (assuming they may happen in your data).
I left the customer id as a string; if it must be a number, it may be better to wrap it all within to_number().
with
test_data ( str ) as (
select 'Ashu||123||Main Menu|ENG||1|1' from dual union all
select 'Misha||125' from dual union all
select 'Babu||||Main Menu|NZL||?' from dual union all
select 'Rim||' from dual union all
select 'Todd' from dual union all
select '||139||Other Stuff' from dual
)
-- end of test data (only for testing and illustration) - not part of solution
-- SQL query begins BELOW THIS LINE
select str,
regexp_substr(str, '([^|]*)(\|\||$)', 1, 1, null, 1) as cust_name,
regexp_substr(str, '([^|]*)(\|\||$)', 1, 2, null, 1) as cust_id
from test_data
;
STR CUST_NAME CUST_ID
----------------------------- --------- -------
Ashu||123||Main Menu|ENG||1|1 Ashu 123
Misha||125 Misha 125
Babu||||Main Menu|NZL||? Babu
Rim|| Rim
Todd Todd
||139||Other Stuff 139
6 rows selected.

Get names for IDs in two columns in one from another table

I have two tables:
CREATE TABLE tElements (
elementID INTEGER,
name TEXT,
area TEXT,
zone TEXT,
voltageLevel TEXT,
mRID TEXT
);
CREATE TABLE tCAResults (
timestamp INTEGER NOT NULL,
outageElementID INTEGER NOT NULL,
monitoredElementID INTEGER NOT NULL,
preOutageLoading DOUBLE NOT NULL,
postOutageLoading DOUBLE NOT NULL
);
How can I create query where id's of outageElementID and monitoredElementID from table tCAResult would be displayed as names from table tElements?
I have been searching for a whole day but couldn't find the answer. The closest I found is this but can't work it out
A simple join or two will do the job:
select tc.timestamp, oe.name as outageElement, me.name as monitoredElement
from tCAResults tc
join tElements oe on (oe.elementID = tc.outageELementID)
join tElements me on (me.elementID = tc.monitoredElementID);

Resources