Insert into table some values which are selected from other table - sql

I have my database structure like this ::
Database structure ::
ATT_table- ActID(PK), assignedtoID(FK), assignedbyID(FK), Env_ID(FK), Product_ID(FK), project_ID(FK), Status
Product_table - Product_ID(PK), Product_name
Project_Table- Project_ID(PK), Project_Name
Environment_Table- Env_ID(PK), Env_Name
Employee_Table- Employee_ID(PK), Name
Employee_Product_projectMapping_Table -Emp_ID(FK), Project_ID(FK), Product_ID(FK)
Product_EnvMapping_Table - Product_ID(FK), Env_ID(FK)
I want to insert values in ATT_Table. Now in that table I have some columns like assignedtoID, assignedbyID, envID, ProductID, project_ID which are FK in this table but primary key in other tables they are simply numbers).
Now when I am inputting data from the user I am taking that in form of string like a user enters Name (Employee_Table), product_Name (Product_table) and not ID directly. So I want to first let the user enter the name (of Employee or product or Project or Env) and then value of its primary key (Emp_ID, product_ID, project_ID, Env_ID) are picked up and then they are inserted into ATT_table in place of assignedtoID, assignedbyID, envID, ProductID, project_ID.
Please note that assignedtoID, assignedbyID are referenced from Emp_ID in Employee_Table.
How to do this ? I have got something like this but its not working ::
INSERT INTO ATT_TABLE(Assigned_To_ID,Assigned_By_ID,Env_ID,Product_ID,Project_ID)
VALUES (A, B, Env_Table.Env_ID, Product_Table.Product_ID, Project_Table.Project_ID)
SELECT Employee_Table.Emp_ID AS A,Employee_Table.Emp_ID AS B, Env_Table.Env_ID, Project_Table.Project_ID, Product_Table.Product_ID
FROM Employee_Table, Env_Table, Product_Table, Project_Table
WHERE Employee_Table.F_Name= "Shantanu" or Employee_Table.F_Name= "Kapil" or Env_Table.Env_Name= "SAT11A" or Product_Table.Product_Name = "ABC" or Project_Table.Project_Name = "Project1";

The way this is handled is by using drop down select lists. The list consists of (at least) two columns: one holds the Id's teh database works with, the other(s) store the strings the user sees. Like
1, "CA", "Canada"
2, "USA", 'United States"
...
The user sees
CA | Canada
USA| United States
...
The value that gets stored in the database is 1, 2, ... whatever row the user selected.
You can never rely on the exact, correct input of users. Sooner or later they will make typo's.
I extend my answer, based on your remark.
The problem with the given solution (get the Id's from the parent tables by JOINing all those parent tables together by the entered text and combining those with a number of AND's) is that as soon as one given parameter has a typo, you will get not a single record back. Imagine the consequences when the real F_name of the employee is "Shant*anu*" and the user entered "Shant*aun*".
The best way to cope with this is to get those Id's one by one from the parent tables. Suppose some FK's have a NOT NULL constraint. You can check if the F_name is filled in and inform the user when he didn't fill that field. Suppose the user eneterd "Shant*aun*" as name, the program will not warn the user, as something is filled in. But that is not the check the database will do, because the NOT NULL constraints are defined on the Id's (FK). When you get the Id's one by one from the parent tables. You can verify if they are NOT NULL or not. When the text is filled in, like "Shant*aun*", but the returned Id is NULL, you can inform the user of a problem and let him correct his input: "No employee by the name 'Shantaun' could be found."
SELECT $Emp_ID_A = Emp_ID
FROM Employee_Table
WHERE F_Name= "Shantanu"
SELECT $Emp_ID_B = Emp_ID
FROM Employee_Table
WHERE B.F_Name= "Kapil"
SELECT $Env_ID = Env_ID
FROM Env_Table
WHERE Env_Table.Env_Name= "SAT11A"
SELECT $Product_ID = Product_ID
FROM Product_Table
WHERE Product_Table.Product_Name = "ABC"
SELECT $Project_ID = Project_ID
FROM Project_Table
WHERE Project_Name = "Project1"

Please use AND instead of OR.
INSERT INTO ATT_TABLE(Assigned_To_ID,Assigned_By_ID,Env_ID,Product_ID,Project_ID)
SELECT A.Emp_ID, B.Emp_ID, Env_Table.Env_ID, Project_Table.Project_ID, Product_Table.Product_ID
FROM Employee_Table A, Employee_Table B, Env_Table, Product_Table, Project_Table
WHERE A.F_Name= "Shantanu"
AND B.F_Name= "Kapil"
AND Env_Table.Env_Name= "SAT11A"
AND Product_Table.Product_Name = "ABC"
AND Project_Table.Project_Name = "Project1";
But it is best practice to use drop down list in your scenario, i guess.

Related

SQL: combine two tables for a query

I want to query two tables at a time to find the key for an artist given their name. The issue is that my data is coming from disparate sources and there is no definitive standard for the presentation of their names (e.g. Forename Surname vs. Surname, Forename) and so to this end I have a table containing definitive names used throughout the rest of my system along with a separate table of aliases to match the varying styles up to each artist.
This is PostgreSQL but apart from the text type it's pretty standard. Substitute character varying if you prefer:
create table Artists (
id serial primary key,
name text,
-- other stuff not relevant
);
create table Aliases (
artist integer references Artists(id) not null,
name text not null
);
Now I'd like to be able to query both sets of names in a single query to obtain the appropriate id. Any way to do this? e.g.
select id from ??? where name = 'Bloggs, Joe';
I'm not interested in revising my schema's idea of what a "name" is to something more structured, e.g. separate forename and surname, since it's inappropriate for the application. Most of my sources don't structure the data, sometimes one or the other name isn't known, it may be a pseudonym, or sometimes the "artist" may be an entity such as a studio.
I think you want:
select a.id
from artists a
where a.name = 'Bloggs, Joe' or
exists (select 1
from aliases aa
where aa.artist = a.id and
aa.name = 'Bloggs, Joe'
);
Actually, if you just want the id (and not other columns), then you can use:
select a.id
from artists a
where a.name = 'Bloggs, Joe'
union all -- union if there could be duplicates
select aa.artist
from aliases aa
where aa.name = 'Bloggs, Joe';

Using sql EXCEPT to build an EAVT fact store

I'm exploring the datomic database, and in so doing having a go at taking some of its ideas and implementing them in sql in an incremental way so as to adjust to the new ways of data modelling. This question is really entirely about SQL though, I just mention that for background, to explain the why of what I'm doing here (though might be interesting for those interested in datomic too, which is why I also added the datomic tag to the question).
Generally we are getting rid of separate tables per type, but I will retain a users table for this example, rather than simply use an entities table (may try that later, but not yet).
create table users (
id uuid,
identity text -- e.g. 'the yankees', 'man born as john in birmingham on date x/y/z'
);
Then we have an EAVT store, also with an added boolean to specify add or retract. This table is append only. We will never issue update or delete on it.
create table eavt_log (
user_id uuid,
attribute text,
value text,
added boolean,
created_at timestamp
);
Now some data to illustrate usage intended
-- insert person number 12345 (imagine as national identity or birth certificate no.)
insert into users(id, identity) values (uuid_generate_v4(), 'p-12345');
-- lets insert some facts about a person previously known as john smith:
insert into eavt_log(user_id, attribute, value, added, created_at) values
((select id from users where identity='p-12345'),
'name', 'John Smith', true, '1911-01-01'),
((select id from users where identity='p-12345'),
'name', 'John Smith', false, '1931-01-01'),
((select id from users where identity='p-12345'),
'name', 'John Bontine Smith', true, '1931-01-01');
To make this useful (any database must provide leverage, as Hickey says), lets try to find all the unretracted names for the person previously known as John Smith.
Here's my (bad) attempt
-- find all currently unretracted names for person previously known as John Smith. This could
-- be 0, 1 (we hope), or more - it just depends though, and should, on what data has been input.
(select attribute, value from eavt_log
where user_id = (select id from users where identity='p-12345')
and attribute = 'name'
and added = true
order by created_at desc) -- <- can sneak this in w/o upsetting the except, as it's not in the select.
except
(select attribute, value from eavt_log
where user_id = (select id from users where identity='p-12345')
and attribute = 'name'
and added = false);
That gives:
attribute | value
-----------+--------------------
name | John Bontine Smith
(1 row)
Which is correct for the test data we gave it.
Then we can try to generalise to
create view unretracted as (
(select user_id, attribute, value from eavt_log
where added = true
order by created_at)
except
(select user_id, attribute, value from eavt_log
where added = false)
);
Problem is, both of these are flawed, because this simple except will give incorrect result for the case when a fact has been added, retracted, then added again. i.e. if we add
((select id from users where identity='p-12345'),
'name', 'John Smith', false, '1941-01-01');
to the facts inserted above, to denote that person-12345, in 1941, adopted the name John Smith again (without retracting the name 'John Bontine Smith', so in this case we want the system to return two values for his name).
With this data, the earlier retract of this identical value will cause this later re-assertion of the same value to be excluded from the result set, even though its been reasserted, due to the way EXCEPT is working (we did not do a linear table scan which i think may be required here?)
My question (finally!) -- is there a way to achieve this in SQL? Can SQL give us more leverage here?
It seems as if we need a where after the except which reaches back into the first select... but that seems impossible in set theory terms, so I wonder what else SQL can do here.
This is edited for your update, although I think there is still something wrong. You added an additional retracted row, which seems to contradict your text. Assuming that the row is actually added instead of retracted, we can use the below query.
You can use DISTINCT ON in postgres to get the last value per user. If you use that in a sub-select, you can only select the rows for which added = true:
SELECT attribute, value
FROM (
SELECT distinct on (eavt_log.user_id, attribute, value)
attribute, value, added
FROM eavt_log
JOIN users ON eavt_log.user_id = users.id
WHERE attribute = 'name'
ORDER BY eavt_log.user_id, attribute, value, created_at desc) sub
WHERE added = 't';
Edit: here's a fiddle

Inserting multiple records in database table using PK from another table

I have DB2 table "organization" which holds organizations data including the following columns
organization_id (PK), name, description
Some organizations are deleted so lot of "organization_id" (i.e. rows) doesn't exist anymore so it is not continuous like 1,2,3,4,5... but more like 1, 2, 5, 7, 11,12,21....
Then there is another table "title" with some other data, and there is organization_id from organization table in it as FK.
Now there is some data which I have to insert for all organizations, some title it is going to be shown for all of them in web app.
In total there is approximately 3000 records to be added.
If I would do it one by one it would look like this:
INSERT INTO title
(
name,
organization_id,
datetime_added,
added_by,
special_fl,
title_type_id
)
VALUES
(
'This is new title',
XXXX,
CURRENT TIMESTAMP,
1,
1,
1
);
where XXXX represent "organization_id" which I should get from table "organization" so that insert do it only for existing organization_id.
So only "organization_id" is changing matching to "organization_id" from table "organization".
What would be best way to do it?
I checked several similar qustions but none of them seems to be equal to this?
SQL Server 2008 Insert with WHILE LOOP
While loop answer interates over continuous IDs, other answer also assumes that ID is autoincremented.
Same here:
How to use a SQL for loop to insert rows into database?
Not sure about this one (as question itself is not quite clear)
Inserting a multiple records in a table with while loop
Any advice on this? How should I do it?
If you seriously want a row for every organization record in Title with the exact same data something like this should work:
INSERT INTO title
(
name,
organization_id,
datetime_added,
added_by,
special_fl,
title_type_id
)
SELECT
'This is new title' as name,
o.organization_id,
CURRENT TIMESTAMP as datetime_added,
1 as added_by,
1 as special_fl,
1 as title_type_id
FROM
organizations o
;
you shouldn't need the column aliases in the select but I am including for readability and good measure.
https://www.ibm.com/support/knowledgecenter/ssw_i5_54/sqlp/rbafymultrow.htm
and for good measure in case you process errors out or whatever... you can also do something like this to only insert a record in title if that organization_id and title does not exist.
INSERT INTO title
(
name,
organization_id,
datetime_added,
added_by,
special_fl,
title_type_id
)
SELECT
'This is new title' as name,
o.organization_id,
CURRENT TIMESTAMP as datetime_added,
1 as added_by,
1 as special_fl,
1 as title_type_id
FROM
organizations o
LEFT JOIN Title t
ON o.organization_id = t.organization_id
AND t.name = 'This is new title'
WHERE
t.organization_id IS NULL
;

Maximo - Return multiple values based on another field using List Where Clause in Domain

I know I can't return multiple values with the Case statement but this is the best way I can explain what I want to accomplish. I am trying to write a statement where I will return different values based on what is entered in another field. I have something like this currently:
SELECT animal WHERE
CASE
WHEN :textbox is not null
THEN (SELECT animal from animalsTable where animalType = :textbox
ELSE (SELECT plant from plantsTable where plantType = 'edible')
So basically, I want to be able to list all the animals that correspond to what the user types in the textbox, but if they do not enter anything in the textbox, then I want to show them all plants that are edible instead. I almost always going to return multiple values for each value they enter.
For example, if the user types 'dog' then i will return 'dog' and 'wolf'. So this causes a problem since the case statement is boolean. How can I get around this?
Thanks.
You can create an ALN Domain that contains all your lookup value list. In this case, both animals and plants. Then create a table domain that references ALN domain based on the key field you want to filter by. You'll need store this key in the Description field as a single value, or multiple values separated by a space or comma.
For us, we used a custom field (subcategory) that displayed a limited lookup from the ALN Domain using a Table domain filtering on the asset department number. The Asset Department Number is listed in the ALN Domain description.
ALN domain contains your plant and animal values. Description of ALN domain contains your key field vale.
If the Asset Department is empty, then entire list shows up.
The list where clause looks something like this:
domainid='CBRSUBCAT' and description like '%' || (select eq5 from asset where assetnum = :assetnum) || '%'
I used a like so we could enter multiple departments for one subcategory separated by comma. For you, you could use description = (equals) if you wanted.
You could check the textbox value for each table and union the results:
select animal from animalsTable where animalType = :textbox
and :textbox is not null
union all
select plant from plantsTable where plantType = 'edible'
and :textbox is null

Underlying rows in Group By

I have a table with a certain number of columns and a primary key column (suppose OriginalKey). I perform a GROUP BY on a certain sub-set of those columns and store them in a temporary table with primary key (suppose GroupKey). At a later stage, I may need to get more details about one or more of those groupings (which can be found in the temporary table) i.e. I need to know which were the rows from the original table that formed that group. Simply put, I need to know the mappings between GroupKey and OriginalKey. What's the best way to do this? Thanks in advance.
Example:
Table Student(
StudentID INT PRIMARY KEY,
Level INT, --Grade/Class/Level depending on which country you are from)
HomeTown TEXT,
Gender CHAR)
INSERT INTO TempTable SELECT HomeTown, Gender, COUNT(*) AS NumStudents FROM Student GROUP BY HomeTown, Gender
On a later date, I would like to find out details about all towns that have more than 50 male students and know details of every one of them.
How about joining the 2 tables using the GroupKey, which, you say, are the same?
Or how about doing:
select * from OriginalTable where
GroupKey in (select GroupKey from my_temp_table)
You'd need to store the fields you grouped on in your temporary table, so you can join back to the original table. e.g. if you grouped on fieldA, fieldB, and fieldC, you'd need something like:
select original.id
from original
inner join temptable on
temptable.fieldA = original.fieldA and
temptable.fieldB = original.fieldB and
temptable.fieldC = original.fieldC