SQL INSERT/SELECT using WITH clause - sql

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).

Related

Insert results from select query, which is already changed

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?

HAVING clause with subquery -- Checking if group has at least one row matching conditions

Suppose I have the following table
DROP TABLE IF EXISTS #toy_example
CREATE TABLE #toy_example
(
Id int,
Pet varchar(10)
);
INSERT INTO #toy
VALUES (1, 'dog'),
(1, 'cat'),
(1, 'emu'),
(2, 'cat'),
(2, 'turtle'),
(2, 'lizard'),
(3, 'dog'),
(4, 'elephant'),
(5, 'cat'),
(5, 'emu')
and I want to fetch all Ids that have certain pets (for example either cat or emu, so Ids 1, 2 and 5).
DROP TABLE IF EXISTS #Pets
CREATE TABLE #Pets
(
Animal varchar(10)
);
INSERT INTO #Pets
VALUES ('cat'),
('emu')
SELECT Id
FROM #toy_example
GROUP BY Id
HAVING COUNT(
CASE
WHEN Pet IN (SELECT Animal FROM #Pets)
THEN 1
END
) > 0
The above gives me the error Cannot perform an aggregate function on an expression containing an aggregate or a subquery. I have two questions:
Why is this an error? If I instead hard code the subquery in the HAVING clause, i.e. WHEN Pet IN ('cat','emu') then this works. Is there a reason why SQL server (I've checked with SQL server 2017 and 2008) does not allow this?
What would be a nice way to do this? Note that the above is just a toy example. The real problem has many possible "Pets", which I do not want to hard code. It would be nice if the suggested method could check for multiple other similar conditions too in a single query.
If I followed you correctly, you can just join and aggregate:
select t.id, count(*) nb_of_matches
from #toy_example t
inner join #pets p on p.animal = t.pet
group by t.id
The inner join eliminates records from #toy_example that have no match in #pets. Then, we aggregate by id and count how many recors remain in each group.
If you want to retain records that have no match in #pets and display them with a count of 0, then you can left join instead:
select t.id, count(*) nb_of_records, count(p.animal) nb_of_matches
from #toy_example t
left join #pets p on p.animal = t.pet
group by t.id
How about this approach?
SELECT e.Id
FROM #toy_example e JOIN
#pets p
ON e.pet = p.animal
GROUP BY e.Id
HAVING COUNT(DISTINCT e.pet) = (SELECT COUNT(*) FROM #pets);

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
)

Join on resultant table of another join without using subquery,CTE or temp tables

My question is can we join a table A to resultant table of inner join of table A and B without using subquery, CTE or temp tables ?
I am using SQL Server.
I will explain the situation with an example
The are two tables GoaLScorers and GoalScoredDetails.
GoaLScorers
gid Name
-----------
1 A
2 B
3 A
GoalScoredDetails
DetailId gid stadium goals Cards
---------------------------------------------
1 1 X 2 1
2 2 Y 5 2
3 3 Y 2 1
The result I am expecting is if I select a stadium 'X' (or 'Y')
I should get name of all who may or may not have scored there, also aggregate total number of goals,total cards.
Null value is acceptable for names if no goals or no cards.
I can get the result I am expecting with the below query
SELECT
gs.name,
SUM(goal) as TotalGoals,
SUM(cards) as TotalCards
FROM
(SELECT
gid, stadium, goal, cards
FROM
GoalScoredDetails
WHERE
stadium = 'Y') AS vtable
RIGHT OUTER JOIN
GoalScorers AS gs ON vtable.gid = gs.gid
GROUP BY
gs.name
My question is can we get the above result without using a subquery or CTE or temp table ?
Basically what we need to do is OUTER JOIN GoalScorers to resultant virtual table of INNER JOIN OF GoalScorers and GoalScoredDetails.
But I am always faced with ambiguous column name error as "gid" column is present in GoalScorers and also in resultant table. Error persists even if I try to use alias for column names.
I have created a sql fiddle for this her: http://sqlfiddle.com/#!3/40162/8
SELECT gs.name, SUM(gsd.goal) AS totalGoals, SUM(gsd.cards) AS totalCards
FROM GoalScorers gs
LEFT JOIN GoalScoredDetails gsd ON gsd.gid = gs.gid AND
gsd.Stadium = 'Y'
GROUP BY gs.name;
IOW, you could push your where criteria onto joining expression.
The error Ambiguous column name 'ColumnName' occurs when SQL Server encounters two or more columns with the same and it hasn't been told which to use. You can avoid the error by prefixing your column names with either the full table name, or an alias if provided. For the examples below use the following data:
Sample Data
DECLARE #GoalScorers TABLE
(
gid INT,
Name VARCHAR(1)
)
;
DECLARE #GoalScoredDetails TABLE
(
DetailId INT,
gid INT,
stadium VARCHAR(1),
goals INT,
Cards INT
)
;
INSERT INTO #GoalScorers
(
gid,
Name
)
VALUES
(1, 'A'),
(2, 'B'),
(3, 'A')
;
INSERT INTO #GoalScoredDetails
(
DetailId,
gid,
stadium,
goals,
Cards
)
VALUES
(1, 1, 'x', 2, 1),
(2, 2, 'y', 5, 2),
(3, 3, 'y', 2, 1)
;
In this first example we recieve the error. Why? Because there is more than one column called gid it cannot tell which to use.
Failed Example
SELECT
gid
FROM
#GoalScoredDetails AS gsd
RIGHT OUTER JOIN #GoalScorers as gs ON gs.gid = gsd.gid
;
This example works because we explicitly tell SQL which gid to return:
Working Example
SELECT
gs.gid
FROM
#GoalScoredDetails AS gsd
RIGHT OUTER JOIN #GoalScorers as gs ON gs.gid = gsd.gid
;
You can, of course, return both:
Example
SELECT
gs.gid,
gsd.gid
FROM
#GoalScoredDetails AS gsd
RIGHT OUTER JOIN #GoalScorers as gs ON gs.gid = gsd.gid
;
In multi table queries I would always recommend prefixing every column name with a table/alias name. This makes the query easier to follow, and reduces the likelihood of this sort of error.

Query for a table where FK can be null

A simple case: we have a main table (id, name, dictionary_id) and a dictionary table (id, name). The dictionary_id is the FK related with the dictionary table. NULL values are possible for dictionary_id.
I need to show:
id, name, dictionary_name
if dictionary_id is null (in this case dictionary_name is empty) or
dictionary_id comes from a list (e.g. from subquery).
Thanks,
Jacek
You are looking for an outer join:
select mt.id, mt.name, dt.name as dictionary_name
from main_table mt
left join dictionary_table dt on mt.dictionary_id = dt.id;