Insert results from select query, which is already changed - sql

I want to run this query:
INSERT INTO contacts_fields (
contact_id, field_name, numeric_value
)
select distinct
c.id,
'storeId',
1
from
contacts c
inner join contacts_devices cd on cd.contact_id = c.id
and c.id NOT IN (
SELECT
contact_id
FROM
contacts_fields
)
group by
c.id
But I am getting an error:
SQL Error [23503]: ERROR: insert or update on table "contact_fields" violates foreign key constraint "contacts_fields_fkey"
Detail: Key (contact_id)=(2425542) is not present in table "contacts".
This is propably because contacts table is changing all the time, any maybe I can't insert value with id = 2425542, becasuse it was deleted between select and insert query. What can I do with this?

Related

SQL INSERT/SELECT using WITH clause

I'm trying to make a copy from two tables based on a list of IDs, those IDs are retrieved from an initial table, then those IDs are used into an INSERT SELECT statement, also the created IDs from the previous INSERT must be inserted into a third table:
BEGIN;
WITH dog_tmp AS (SELECT id FROM dog WHERE toy_id = '12345'),
meal_tmp as (INSERT INTO meal (id, dog_id, date_created, type)
SELECT public.uuid_generate_v4(), dt.id, m.date_created, m.type
FROM meal AS m
JOIN dog_tmp dt ON dt.id = m.dog_id RETURNING m.id AS meal_uuid)
INSERT INTO dog_diet (id, meal_id, date_created)
SELECT public.uuid_generate_v4(), mt.meal_uuid, dd.date_created
FROM meal_tmp mt
JOIN dog_diet dd ON dd.thread_id = mt.meal_uuid;
COMMIT;
I'm getting this error: [42P01] ERROR: missing FROM-clause entry for table "m"
Thanks in advance for your hints about this, or if possible another approach.
Quick answer: change RETURNING m.id AS meal_uuid to RETURNING id AS meal_uuid (db fiddle).
So the full statement at issue is:
INSERT INTO meal (id, dog_id, date_created, type)
SELECT public.uuid_generate_v4(), dt.id, m.date_created, m.type
FROM meal AS m
JOIN dog_tmp dt ON dt.id = m.dog_id
RETURNING m.id AS meal_uuid
Within the select you set m as an alias for meal but the scope of this is the SELECT query (so m is not defined within the outer insert statement).

Postgres - Insert timestamps in an INSERT INTO query while selecting values from tables?

I'm trying to run a migration in a Phoenix application, but Postgrex returns the following error:
null value in column "inserted_at" violates not-null constraint
The query that generated that error is:
execute "INSERT INTO contract_groups
(SELECT c.id, fg.group_id FROM contracts c, folder_groups fg
WHERE c.folder_id = fg.folder_id)"
I tried updating the query to this:
execute "INSERT INTO contract_groups
(contract_id, group_id, inserted_at)
VALUES ((SELECT c.id, fg.group_id FROM contracts c, folder_groups fg
WHERE c.folder_id = fg.folder_id), NOW())"
but I get another error saying subquery must return only one column.
This is the rough definition of the tables.
insert into contract_groups (contract_id, group_id, inserted_at)
select c.id, fg.group_id, CURRENT_TIMESTAMP
from contracts c
inner join folder_groups fg
on fg.folder_id = c.folder_id
Note, this relies on the columns selected matching the order of the columns in statement
UPDATE:
As per comment, try:
insert into contract_groups (contract_id, group_id, inserted_at)
select distinct c.id, -- distinct added to prevent duplicates
fg.group_id,
CURRENT_TIMESTAMP
from contracts c
inner join folder_groups fg
on fg.folder_id = c.folder_id
where not exists ( -- exclude any combos that are in the target table
select 1
from contract_groups cg
where cg.contract_id = c.id
and fg.froup_id = cg.group_id
)

INSTEAD OF INSERT trigger & Identity column using view

I'm building a database that will store client data for a company. The tables in the DB are normalized, so I have multiple tables that are linked together using Foreign Key Constraints.
Microsoft Access will be used to interface with the database (as a frontend). To make things simpler, I created a view that joins all the required tables together so that end-users can query information without hassle.
The problem I've run into involves INSERTING information into this view. From my understanding, since I have multiple tables in my view, I have to use a trigger with an INSTEAD OF INSERT statement. I've created the trigger; however, I'm unsure how to work with the ID columns in the tables (these act as keys).
I have a MemberBasicInformation table that contains the client's DOB, Name, Gender, etc AND their MemberID. This ID is an IDENTITY column in the table, so it is generated automatically. The problem that I'm running into is that because the identity is automatically generated, I'm unable to grab the identity value that is generated after the insert into the MemberBasicInformation table and insert it into the other related tables. When I attempt to do so, I end up with a Foreign Key constraint violations.
I've tried using ##Identity and Scope_Identity() to no avail. I've listed my view and trigger to give you an idea of how things are set up. I would greatly appreciate if someone could point me in the right direction. I'm truly at a loss.
MEMBER view:
CREATE VIEW [dbo].[Member]
AS
SELECT
dbo.MemberBasic.MemberId, dbo.MemberBasic.FirstName,
dbo.MemberBasic.MiddleInitial, dbo.MemberBasic.LastName,
dbo.MemberBasic.FullName, dbo.MemberBasic.DateOfBirth,
dbo.Gender.Name AS Gender, dbo.MemberBasic.Address,
dbo.MemberBasic.Address2, dbo.MemberBasic.City,
dbo.MemberBasic.State, dbo.MemberBasic.ZipCode,
dbo.MemberBasic.PhoneNumber,
dbo.MemberBasic.SocialSecurityNumber,
dbo.MemberBasic.DriversLicense,
dbo.MemberBasic.EmployerIdentificationNumber,
dbo.MemberBasic.Notes,
dbo.FieldRep.Name AS FieldRepName,
dbo.MemberDetail.DateAssigned AS FieldRepDateAssigned,
dbo.MemberDetail.CPReceivedOn, dbo.MemberDetail.CredentialedOn,
dbo.MemberEligibility.IsActive, dbo.ICO.Name AS ICO,
dbo.MemberEligibility.StartDate AS EligibilityStartDate,
dbo.MemberEligibility.EndDate AS EligibilityEndDate,
dbo.MemberWorkerCompDetail.ExpirationDate AS WorkerCompExpirationDate,
dbo.MemberWorkerCompDetail.AuditDate AS WorkerCompAuditDate,
dbo.WorkerCompTier.Name AS WorkerCompTier,
dbo.MemberAttachment.AttachmentId,
dbo.MemberAttachment.Data AS AttachmentData
FROM
dbo.MemberAttachment
INNER JOIN
dbo.MemberBasic ON dbo.MemberAttachment.MemberId = dbo.MemberBasic.MemberId
INNER JOIN
dbo.MemberCaregiverAssignment ON dbo.MemberAttachment.MemberId = dbo.MemberCaregiverAssignment.MemberId
INNER JOIN
dbo.MemberDetail ON dbo.MemberBasic.MemberId = dbo.MemberDetail.MemberId
INNER JOIN
dbo.MemberEligibility ON dbo.MemberAttachment.MemberId = dbo.MemberEligibility.MemberId
INNER JOIN
dbo.MemberWorkerCompDetail ON dbo.MemberAttachment.MemberId = dbo.MemberWorkerCompDetail.MemberId
INNER JOIN
dbo.Gender ON dbo.MemberBasic.GenderId = dbo.Gender.GenderId
INNER JOIN
dbo.FieldRep ON dbo.MemberDetail.FieldRepId = dbo.FieldRep.FieldRepId
INNER JOIN
dbo.ICO ON dbo.MemberEligibility.ICOId = dbo.ICO.ICOId
INNER JOIN
dbo.WorkerCompTier ON dbo.MemberWorkerCompDetail.TierId = dbo.WorkerCompTier.TierId
GO
MEMBER trigger:
ALTER TRIGGER [dbo].[InsertNewMember]
ON [dbo].[Member]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO MemberBasic (FirstName, MiddleInitial, LastName, GenderId, DateOfBirth, Address, Address2, City, State, ZipCode, PhoneNumber, SocialSecurityNumber, DriversLicense, EmployerIdentificationNumber, Notes)
SELECT
FirstName, MiddleInitial, LastName, GenderId, DateOfBirth,
Address, Address2, City, State, ZipCode, PhoneNumber,
SocialSecurityNumber, DriversLicense,
EmployerIdentificationNumber, Notes
FROM
inserted
INNER JOIN
Gender ON Gender.Name = Gender;
INSERT INTO MemberDetail (MemberId, FieldRepId, DateAssigned, CPReceivedOn, CredentialedOn)
SELECT
MemberId, FieldRep.FieldRepId, FieldRepDateAssigned,
CPReceivedOn, CredentialedOn
FROM
inserted
INNER JOIN
FieldRep ON FieldRep.Name = FieldRepName;
INSERT INTO MemberEligibility (MemberId, ICOId, StartDate, EndDate)
SELECT
MemberId, ICOId, EligibilityStartDate, EligibilityEndDate
FROM
inserted
INNER JOIN
ICO ON ICO.Name = ICO;
INSERT INTO MemberWorkerCompDetail (MemberId, AuditDate, ExpirationDate, TierId)
SELECT
MemberId, WorkerCompAuditDate, WorkerCompExpirationDate, TierId
FROM
inserted
INNER JOIN
WorkerCompTier ON WorkerCompTier.Name = WorkerCompTier;
INSERT INTO MemberAttachment (MemberId, Data)
SELECT MemberId, AttachmentData
FROM Member
END
You can do this by using a table variable to hold the newly inserted IDs and all of the data that you're going to insert into the other tables. You need to do this because there's no way from just the first INSERTs data to join back to inserted in such a manner that you can match up the IDENTITY values with the rows that caused them to be generated.
We also have to abuse MERGE since INSERT doesn't let you include anything other than the target table in its OUTPUT clause.
I'm doing this on a toy example but hopefully you can see how to write it for your full table structures.
First, some tables:
create table dbo.Core (
ID int IDENTITY(-71,3) not null,
A varchar(10) not null,
constraint PK_Core PRIMARY KEY (ID)
)
go
create table dbo.Child1 (
ID int IDENTITY(-42,19) not null,
ParentID int not null,
B varchar(10) not null,
constraint PK_Child1 PRIMARY KEY (ID),
constraint FK_Child1_Core FOREIGN KEY (ParentID) references Core(ID)
)
go
create table dbo.Child2 (
ID int IDENTITY(-42,19) not null,
ParentID int not null,
C varchar(10) not null,
constraint PK_Child2 PRIMARY KEY (ID),
constraint FK_Child2_Core FOREIGN KEY (ParentID) references Core(ID)
)
go
And the view:
create view dbo.Together
with schemabinding
as
select
c.ID,
c.A,
c1.B,
c2.C
from
dbo.Core c
inner join
dbo.Child1 c1
on
c.ID = c1.ParentID
inner join
dbo.Child2 c2
on
c.ID = c2.ParentID
go
And finally the trigger:
create trigger Together_T_I
on dbo.Together
instead of insert
as
set nocount on
declare #tmp table (ID int not null, B varchar(10) not null, C varchar(10) not null);
merge into dbo.Core c
using inserted i
on
c.ID = i.ID
when not matched then insert (A) values (i.A)
output
inserted.ID /* NB - This is the output clauses inserted,
not the trigger's inserted so this is now populated */
,i.B,
i.C
into #tmp;
insert into dbo.Child1(ParentID,B)
select ID,B
from #tmp
insert into dbo.Child2(ParentID,C)
select ID,C
from #tmp
(And I would keep something like that comment in there since statements inside triggers that include OUTPUT clauses tend to be quite confusing since there are two inserted tables in play)
(It's also noteworthy that it doesn't really matter what you put in the ON clause of the MERGE, so long as you're sure that it will fail to make a match. You may prefer to have, say, just 1=0 if you think it makes it clearer. I just went cute on the fact that the trigger's inserted.ID will be NULL)
And now we do our insert:
insert into dbo.Together(A,B,C) values ('aaa','bbb','ccc')
go
select * from dbo.Together
And get the result:
ID A B C
----------- ---------- ---------- ----------
-71 aaa bbb ccc
Here is answer to your question "Getting Identity values to use as FK in an INSTEAD OF trigger"
https://dba.stackexchange.com/questions/34258/getting-identity-values-to-use-as-fk-in-an-instead-of-trigger

SELECT Statement for cocktail db

This is probably pretty simple and dumb to ask but Im just not getting there right now. I have a db for cocktails and want to check which cocktails I can make with the available ingredients:
Get the names of all cocktails where every ingredient is in stock
These are my tables:
create table cocktails
(
name TEXT PRIMARY KEY
)
create table ingredients
(
name TEXT PRIMARY KEY
)
create table cocktail_ingredients
(
cocktail_name TEXT ,
ingredient_name TEXT ,
amount INTEGER ,
FOREIGN KEY ( cocktail_name ) REFERENCES cocktails( name ) ,
FOREIGN KEY ( ingredient_name ) REFERENCES ingredients( name )
)
create table ingredients_in_stock
(
ingredient_name TEXT ,
FOREIGN KEY ( ingredient_name ) REFERENCES ingredients ( name )
)
And this is my code so far:
SELECT ci.cocktail_name
FROM cocktail_ingredients ci
WHERE ci.ingredient_name IN ( SELECT iis.ingredient_name
FROM ingredients_in_stock iis
)
GROUP BY ci.cocktail_name
HAVING COUNT(*) = ( SELECT COUNT(*)
FROM ingredients_in_stock
)
;
You can use a LEFT JOIN and a IN clause for this. Something like this:
SELECT name FROM cocktails WHERE Name NOT IN(
SELECT DISTINCT ci.cocktail_name FROM cocktail_ingredients ci LEFT JOIN ingredients_in_stock istk
ON ci.ingredient_name=istk.ingredient_name WHERE istk.ingredient_name IS NULL)
This query inverts the logic: List the cocktails where none of it's ingredients are missing on the ingredients_in_stock table. Hope the idea helps you
A correlated subquery should work:
select cocktail_name as all_ingredients_in_stock
from cocktail_ingredients ci
inner join ingredients_in_stock iis
on ci.ingredient_name = iis.ingredient_name
group by cocktail_name
having count(*) =
(select count(*)
from cocktail_ingredients
where cocktail_name = ci.cocktail_name
)
Sample SQL Fiddle
You could just say something like this:
select ci.name
from cocktail_ingredients ci
left join ingredients_in_stock iis on iis.ingredient_name = ci.ingredient_name
group by ci.name
having count(ci.ingredient_name) = sum( case
when iis.ingredient_name is not null
then 1
else 0
end
)
In the having clause,
The count(ci.ingredient_name) gives you the total number of ingredients required for the cocktail
The sum() expression gives you the count of in-stock ingredients used by the cocktail.

sql server insert data from table1 to table2 where table1=table3 data

I am trying to insert into Table A, unique data from Table B that matches data in TAble C but I am keep getting the violation of primary key error and not sure what I am doing wrong
Table A - bookfeed
Table B - bookF
Table C - bookStats
INSERT INTO bookFeed
(entryId,feed,entryContent,pubDate,authorName,authorId,age,
sex,locale,pic,fanPage, faceTitle,feedtype,searchterm,clientId,dateadded)
SELECT DISTINCT b.entryId,b.feed,b.entryContent,b.pubDate,b.authorName,
b.authorId,b.age,b.sex,b.locale,b.pic,b.fanPage,b.faceTitle,b.feedtype,
b.searchterm, b.clientId,b.dateadded
FROM bookF as b
INNER JOIN bookStats as a on a.feed = b.feed
WHERE NOT EXISTS (SELECT *
FROM bookFeed as c
WHERE c.entryId = b.entryId)
Table A bookFeed has a primary key on entryId
It looks like in table bookF, there are duplicate records per entryId
If you only want one entryId (limited by PK on bookFeed), you can use this. Adjust the order by in the ROW_NUMBER to suit
insert into bookFeed (
entryId,feed,entryContent,pubDate,authorName,authorId,age,
sex,locale,pic,fanPage,faceTitle,feedtype,searchterm,clientId,dateadded)
select
entryId,feed,entryContent,pubDate,authorName,authorId,age,
sex,locale,pic,fanPage,faceTitle,feedtype,searchterm, clientId,dateadded
from
(
select rn=Row_number() over (partition by b.entryId order by b.entryId),
b.entryId,b.feed,b.entryContent,b.pubDate,b.authorName,b.authorId,b.age,
b.sex,b.locale,b.pic,b.fanPage,b.faceTitle,b.feedtype,b.searchterm, b.clientId,b.dateadded
from bookF as b
inner join bookStats as a on a.feed = b.feed
left join bookFeed as c on c.entryId=b.entryId
where c.entryId is null
) X
where rn=1
UPDATE : Try this for you query and see if it works,look at the data and see if there are duplicates meaning all the entries should have an entry id different than what is currently there -
SELECT b.entryId,b.feed,b.entryContent,b.pubDate,b.authorName,
b.authorId,b.age,b.sex,b.locale,b.pic,b.fanPage,b.faceTitle,b.feedtype,
b.searchterm, b.clientId,b.dateadded
FROM bookF as b
INNER JOIN bookStats as a on a.feed = b.feed
WHERE b.entryId IN (SELECT distinct entryid
FROM bookFeed)
I think you are trying to insert an entry id which is an primary key(check if the value trying to insert is not an duplicate,null or violates any other of primary key constraint)...so either dont try to insert it if it gets populated automatically or in case you are looking to insert it then turn on identity insert on..and try again...
But ideally your id should be calculated (auto increment or something) and never be inserted directly.