With postgreSQL I would like to ...
Goal: I would like to do a subtraction operation on two tables that were joined (INNER JOIN) and grouped (GROUP BY) before.
Below is a minimal reproducible script that I hope will work for you.
In this script I create the tables, insert data, show what I expect with a workaround. And I show you what I'd like to do in a single SQL operation (unsuccessfully).
I hope you understand and thank you for your attention.
/* Scrip minimal reproducible example (Script_mre)
* Data: 04/01/2023
* Autor: Carlos Antonio Zarzar */
-- Purpose: I would like to do a subtraction operation on two tables that
-- were joined (INNER JOIN) and grouped (GROUP BY) before.
-------------------#-------------------#-------------------#-------------------#
--## Creating database and tables ##--
-- Building Database
CREATE DATABASE db_racao;
-- TABLE racao
CREATE TABLE racao(
id_racao SERIAL PRIMARY KEY NOT NULL,
tamanho INT NOT NULL,
tipo VARCHAR(20) NOT NULL,
proteina INT NOT NULL
);
-- TABLE compra_racao
CREATE TABLE compra_racao(
id_comp_racao SERIAL PRIMARY KEY NOT NULL,
id_racao SERIAL NOT NULL REFERENCES racao(id_racao),
valor_uni NUMERIC NOT NULL,
quantidade REAL NOT NULL,
valor_entrada NUMERIC NOT NULL,
validade DATE NOT NULL,
cod_lote INT NOT NULL
);
-- TABLE saida_racao
CREATE TABLE saida_racao(
id_saida_racao SERIAL PRIMARY KEY NOT NULL,
quantidade REAL NOT NULL,
valor_saida NUMERIC NOT NULL,
data_saida TIMESTAMP NOT NULL,
id_comp_racao SERIAL NOT NULL REFERENCES compra_racao(id_comp_racao),
id_racao SERIAL NOT NULL REFERENCES racao(id_racao)
);
-------------------#-------------------#-------------------#-------------------#
--## Inserting data into tables ##--
-- TABLE racao
INSERT INTO racao(tamanho,tipo,proteina)
VALUES
(5,'alevino',48),
(10,'engorda',38),
(25,'prime',42),
(5,'alevino',48);
-- TABLE compra_racao
INSERT INTO compra_racao(id_racao,valor_uni,quantidade,valor_entrada,validade,cod_lote)
VALUES
(1,2.5,2000,5000,'2025-01-01',123),
(2,3.4,1000,3400,'2025-01-01',321),
(3,4.0,1000,4000,'2025-01-01',654),
(1,2.5,4000,10000,'2025-01-01',456),
(2,3.4,2000,6800,'2025-01-01',987),
(3,4.0,1500,6000,'2025-01-01',789),
(4,2.5,2500,6250,'2025-01-01',789);
-- TABLE saida_racao
INSERT INTO saida_racao(quantidade,valor_saida,data_saida,id_comp_racao,id_racao)
VALUES
(2000,5000,'2022-03-05 00:00:00',1,1),
(1000,3400,'2022-05-08 00:00:00',2,2),
(500,1700,'2022-09-25 00:00:00',3,3),
(100,340,'2022-09-25 00:00:00',3,3),
(1000,2500,'2023-02-10 00:00:00',4,1),
(1000,2500,'2023-03-30 00:00:00',5,2),
(1000,2500,'2023-04-05 00:00:00',6,3),
(575,1437.5,'2023-11-10 00:00:00',4,1),
(1525,3812.5,'2023-12-15 00:00:00',4,1),
(1000,2500,'2023-12-20 00:00:00',7,4),
(1200,3000,'2023-12-20 00:00:00',7,4);
-------------------#-------------------#-------------------#-------------------#
--## Making the queries ##--
/* The problem:
* I would like to subtract two resulting tables, the "Entrada" table and the "Saida" table.
* Each tables are grouped by id.racao.*/
/* Not an "elegant" solution, i.e. a workaround:
* I transform each table resulting from the operation into another table (of the MATERIALIZED VIEW type).
* And then I do the third operation which is the query of the "Estoque" table*/
-- MATERIALIZED VIEW Entrada
CREATE MATERIALIZED VIEW view_entrada AS
SELECT r.id_racao, SUM(cr.quantidade) AS "entrada", SUM(cr.valor_entrada) AS "valor_entrada"
FROM racao AS r
INNER JOIN compra_racao AS cr
ON r.id_racao = cr.id_racao
GROUP BY r.id_racao
ORDER BY r.id_racao
WITH DATA;
-- MATERIALIZED VIEW Saida
CREATE MATERIALIZED VIEW view_saida AS
SELECT r.id_racao, SUM(sr.quantidade) AS "saida", SUM(sr.valor_saida) AS "valor_saida"
FROM racao AS r
INNER JOIN saida_racao AS sr
ON r.id_racao = sr.id_racao
GROUP BY r.id_racao
ORDER BY r.id_racao
WITH DATA;
-- And finally the query with the "Estoque" Table (joining the two "Entrada" and "Saida" by group)
-- Estoque
SELECT id_racao, ve.entrada - vs.saida AS quant_total, ve.valor_entrada - vs.valor_saida AS valor_total
FROM view_entrada AS ve
INNER JOIN view_saida AS vs
USING (id_racao);
-- This is the result I expect.
-------------------#-------------------#-------------------#-------------------#
-- Now what I would like to do is do all the operations at once and then
-- make the resulting table a MATERIALIZED VIEW for queries.
-- An idea of what you'd like (may help):
SELECT
(SELECT r.id_racao, SUM(cr.quantidade) FROM racao AS r
INNER JOIN compra_racao AS cr
ON r.id_racao = cr.id_racao
GROUP BY r.id_racao)
-
(SELECT r.id_racao, SUM(sr.quantidade)
FROM racao AS r
INNER JOIN saida_racao AS sr
ON r.id_racao = sr.id_racao
GROUP BY r.id_racao)
You can do it this way (Result here)
with entrada as (
SELECT r.id_racao, SUM(cr.quantidade) AS qty, SUM(cr.valor_entrada) AS valor
FROM racao AS r
INNER JOIN compra_racao AS cr
ON r.id_racao = cr.id_racao
GROUP BY r.id_racao
ORDER BY r.id_racao),
salida as (
SELECT r.id_racao, SUM(sr.quantidade) AS qty, SUM(sr.valor_saida) AS valor
FROM racao AS r
INNER JOIN saida_racao AS sr
ON r.id_racao = sr.id_racao
GROUP BY r.id_racao
ORDER BY r.id_racao)
select
coalesce(e.id_racao,s.id_racao) as id, coalesce(e.qty,0) - coalesce(s.qty,0) AS quant_total,
coalesce(e.valor,0) - coalesce(s.valor,0) AS valor_total
from
entrada e left join salida s on e.id_racao = s.id_racao
I have to tables: Register and Price.
CREATE TABLE [dbo].[Register](
[RegisterID] [int] NULL,
[GroupID] [int] NULL,
[TestID] [int] NULL)
CREATE TABLE [dbo].[Price](
[ID] [int] NULL,
[GroupID] [int] NULL,
[Price] [bigint] NULL,
[Status] [bit] NULL)
Assuming information similar to the image above, consider the following query
SELECT Price.*
FROM Register RIGHT OUTER JOIN Price ON Register.GroupID = Price.GroupID
WHERE (Price.Status = 1) AND (Register.TestID = 50)
The output will be displayed as shown below.
My expectation is that the first and second rows of the price table will be displayed. So where is my mistake and how should I change the query to get the right output?
The restriction on the Register table which currently appears in the WHERE clause would have to be moved to the ON clause to get the behavior you want:
SELECT p.*
FROM Register r
RIGHT JOIN Price p ON r.GroupID = p.GroupID AND r.TestID = 50;
WHERE p.Status = 1
Note that more typically you would express the above via a left join:
SELECT p.*
FROM Price p
LEFT JOIN Register r ON r.GroupID = p.GroupID AND r.TestID = 50
WHERE p.Status = 1;
This is my query
SELECT p.book FROM customers_books p
INNER JOIN books b ON p.book = b.id
INNER JOIN bookprices bp ON bp.book = p.book
WHERE b.status = 'PUBLISHED' AND bp.currency_code = 'GBP'
AND p.book NOT IN (SELECT cb.book FROM customers_books cb WHERE cb.customer = 1)
GROUP BY p.book, p.created_date ORDER BY p.created_date DESC
This is the data in my customers_books table,
I expect only 8,6,1 of books IDs to return but query is returning 8,6,1,1
table structures are here
CREATE TABLE "public"."customers_books" (
"id" int8 NOT NULL,
"created_date" timestamp(6),
"book" int8,
"customer" int8,
);
CREATE TABLE "public"."books" (
"id" int8 NOT NULL,
"created_date" timestamp(6),
"status" varchar(255) COLLATE "pg_catalog"."default",
)
CREATE TABLE "public"."bookprices" (
"id" int8 NOT NULL,
"currency_code" varchar(255) COLLATE "pg_catalog"."default",
"book" int8
)
what do you think I am doing wrong here.
I really dont want to use p.created_date in group by but I was forced to use because of order by
You have too many joins in the outer query:
SELECT b.book
FROM books b INNER JOIN
bookprices bp
ON bp.book = p.book
WHERE b.status = 'PUBLISHED' AND bp.currency_code = 'GBP' AND
NOT EXISTS (SELECT 1
FROM customers_books cb
WHERE cb.book = p.book AND cb.customer = 1
) ;
Note that I replaced the NOT IN with NOT EXISTS. I strongly, strongly discourage you from using NOT IN with a subquery. If the subquery returns any NULL values, then NOT IN returns no rows at all. It is better to sidestep this issue just by using NOT EXISTS.
I have two tables:
AppWindowsEvent:
CREATE TABLE [AppWindowsEvent]
(
[idAppWindowEvent] INT IDENTITY(1,1)
, [idAppWindow] INT
, [idEventType] INT
, [Order] INT
, CONSTRAINT PK_idAppWindowEvent PRIMARY KEY ([idAppWindowEvent])
, CONSTRAINT FK_idAppWindowEvent_AppWindow FOREIGN KEY ([idAppWindow]) REFERENCES [AppWindow]([idAppWindow])
, CONSTRAINT FK_idAppWindowEvent_EventType FOREIGN KEY ([idEventType]) REFERENCES [EventType]([idEventType])
)
Event:
CREATE TABLE [Event]
(
[idEvent] [INT] IDENTITY(1,1) NOT NULL
, [idEventType] [INT] NOT NULL
, [idEntity] [INT] NOT NULL
, CONSTRAINT PK_IdEvent PRIMARY KEY([idEvent])
, CONSTRAINT [FK_Event_EventType] FOREIGN KEY([idEventType]) REFERENCES [EventType] ([idEventType])
)
When i run this query:
SELECT
*
FROM
AppWindowsEvent AWE
LEFT JOIN Event E ON AWE.idEventType = E.idEventType
WHERE
AWE.idMill = 1
AND AWE.idAppWindow = 1
ORDER BY
AWE.[Order] ASC
The result: not return nulls.
And when i run this
SELECT
*
FROM
AppWindowsEvent AWE
LEFT JOIN Event E ON AWE.idEventType = E.idEventType
AND E.[idEntity] = 1234
WHERE
AWE.idMill = 1
AND AWE.idAppWindow = 1
ORDER BY
AWE.[Order] ASC
Result: return nulls.
NOTE:
I need the entire set of data that are and are not already configured, in case you want a specific set of events, in the AND of ON can be filtered by specific idEntity of the Event table and the result returns well, but only for that idEntity, in my case I need all idEntity.
Try this
SELECT *
FROM
AppWindowsEvent AWE
LEFT JOIN Event E ON AWE.idEventType = E.idEventType
WHERE
AWE.idMill = 1
AND AWE.idAppWindow = 1
AND E.[idEntity] = 1234
ORDER BY
AWE.[Order] ASC
Or if you doesn't want appear null valor in second table, you can use Inner Join instead Left Join
SELECT *
FROM
AppWindowsEvent AWE
Inner JOIN Event E ON AWE.idEventType = E.idEventType
AND E.[idEntity] = 1234
WHERE
AWE.idMill = 1
AND AWE.idAppWindow = 1
ORDER BY
AWE.[Order] ASC
I'm trying to use an SQL insert statement to migrate rows from a table in one database to a table in a different database. The statement works until I add a unique index on the destination table and at that point I'm struggling to get the insert statement to be able to exclude the duplicates. Here's what I though should work:
INSERT INTO [MyDB].[dbo].[HPB] (
[HPID],
[BusinessID]
)
SELECT
PersonId = (SELECT ID FROM [MyDB].[dbo].[HP] WHERE PersonID = lPersonId),
lBusinessId
FROM [MyOriginalDB].[dbo].[tblEmployment]
WHERE
lPersonId in (SELECT PersonID FROM [MyDB].[dbo].[HP])
AND
lBusinessId in (SELECT ID FROM [MyDB].[dbo].[Business])
AND
NOT EXISTS (SELECT * FROM [MyDB].[dbo].[HPB] WHERE
[HPID] = (SELECT ID FROM [MyDB].[dbo].[HP] WHERE PersonID = lPersonId)
AND [BusinessID] = lBusinessId)
The schema for the HPB table is:
CREATE TABLE [dbo].[HPB](
[ID] [int] IDENTITY(1,1) NOT NULL,
[HPID] [int] NOT NULL,
[BusinessID] [int] NOT NULL,
CONSTRAINT [PK_HealthProfessionalBusiness] PRIMARY KEY CLUSTERED)
The unique index is on the [MyDB].[dbo].[HPB] table for columns (HPID, BusinessID)
When I run the insert I get an error about duplicate row inserts and I can't work out why the SQL below doesn't exclude the duplicates.
NOT EXISTS (SELECT * FROM [MyDB].[dbo].[HPB] WHERE
[HPID] = (SELECT ID FROM [MyDB].[dbo].[HP] WHERE PersonID = lPersonId)
AND [BusinessID] = lBusinessId)
Insert MyDB.dbo.HPB( HPID, BusinessID )
Select HP.ID, E.IBusinessID
From [MyOriginalDB].[dbo].[tblEmployment] As E
Join [MyDB].[dbo].[HP] As HP
On HP.PersonId = E.IPersonID
Join [MyDB].[dbo].[Business] As B
On B.ID = E.IBusinessID
Left Join [MyDB].[dbo].[HPB] As HPB
On HPB.BusinessID = E.IBusinessID
And HPB.PersonID = E.IPersonId
Where HPB.ID Is Null
Group By HP.ID, E.IBusinessID
Use:
INSERT INTO [MyDB].[dbo].[HPB]
([HPID], [BusinessID])
SELECT DISTINCT
h.id,
e.lbusinessid
FROM [MyOriginalDB].[dbo].[tblEmployment] e
JOIN [MyDB].[dbo].[HP] h ON h.personid = e.lpersonid
WHERE e.lbusinessid in (SELECT ID FROM [MyDB].[dbo].[Business])
AND NOT EXISTS (SELECT NULL
FROM [MyDB].[dbo].[HPB] hb
WHERE hb.businessid = e.lbusinessid
AND hb.hpid = h.id)