UPDATE with INNER JOIN - PostgreSQL - postgresql

I have two tables one called customer and the other called referrals I need to add some numbers to two columns in table "referrals" but to know which ones, I need to refer the the table "customer" since its the one that has the group_id.
Here's an example of what I tried
UPDATE referrals AS r
SET total_coins_received = r.total_coins_received + 2, unused_ref_coins = r.unused_ref_coins + 2
FROM customer AS c
WHERE c.group_id = '1' and c.subscription_state = 'active';
This ignored my WHERE state and updated all the fields even if it didn't match the "group_id"
How can I just reference that I need to just change the ones that apply to the conditions?
This is a fiddle with an example of what is happening: https://www.db-fiddle.com/f/4jyoMCicNSZpjMt4jFYoz5/239

Add in the WHERE clause the condition that links the 2 tables:
WHERE c.cust_id = r.cust_id and c.group_id = '1' and c.subscription_state = 'active';
Without the condition c.cust_id = r.cust_id what you get is the cartesian product of referrals and the rows of customer that satisfy the conditions c.group_id = '1' and c.subscription_state = 'active' and this is why all the rows of referrals are updated.

Related

How to return an array from a postgres select query?

I have a table that holds scenario information and whether or not it is active and I query the scenario like so
SELECT
scenario.*,
scenario_detail.name,
scenario_detail.is_activated,
scenario_detail.is_current,
scenario_detail.input_values,
scenario_detail.scenario_id,
scenario_detail.user_id,
scenario_detail.plan_id,
scenario_detail.order_index
FROM scenario
INNER join scenario_detail
ON scenario_detail.scenario_id = scenario.id
WHERE scenario_detail.state = 'ACTIVE'
And it returns the scenarios fine.
Now I would like to return an array that will have two items, a list of all activated scenario and another of inactive scenarios.
I know I could slice and sort these scenarios in the front end but I am rather interested in doing it in the backend.
How could I do it?
If all the fields are same then you can use union.
SELECT
scenario.*,
scenario_detail.name,
scenario_detail.is_activated,
scenario_detail.is_current,
scenario_detail.input_values,
scenario_detail.scenario_id,
scenario_detail.user_id,
scenario_detail.plan_id,
scenario_detail.order_index
FROM scenario
INNER join scenario_detail
ON scenario_detail.scenario_id = scenario.id
WHERE scenario_detail.state = 'ACTIVE'
UNION
SELECT
scenario.*,
scenario_detail.name,
scenario_detail.is_activated,
scenario_detail.is_current,
scenario_detail.input_values,
scenario_detail.scenario_id,
scenario_detail.user_id,
scenario_detail.plan_id,
scenario_detail.order_index
FROM scenario
INNER join scenario_detail
ON scenario_detail.scenario_id = scenario.id
WHERE scenario_detail.state = 'INACTIVE'

How to update multiple fields in two tables using inner join

I've two tables. And I need two fields to update.
they are connected to each other with a foreign key. I would like to update the fields found in these two tables by using my inner join.
UPDATE cert
SET cert.status = 1, doc.status = 1
FROM certificates cert
INNER JOIN documents doc ON doc.data_id = cert.certificate_id
WHERE cert.status = 0 AND cert.user_id = _expert_id AND doc.data_type = 'CERTIFICATE';
The code I mentioned above allows me to update the status field in the certificates table only. In addition, I would like to update the field in the documents table
Maybe this helps:
demo:db<>fiddle
WITH cert_update AS (
UPDATE certificates cert
SET status = 1
FROM documents doc
WHERE doc.data_id = cert.certificate_id
AND cert.status = 0
AND cert.user_id = '2' -- your "_expert_id"
AND doc.data_type = 'CERTIFICATE'
RETURNING certificate_id
)
UPDATE documents doc
SET status = 1
FROM cert_update cert
WHERE doc.data_id = cert.certificate_id
AND doc.data_type = 'CERTIFICATE';
Using the WITH clause (CTE) you are able to do several updates in one query. The first one gives back the updated certificate_id which can be used in the second query.

UPDATE in postgresql with JOINS

I have a form where some input has a value that is made with this query:
SELECT e.tree.nombre
FROM d.p
JOIN e.theme ON id = id_capa
LEFT JOIN e.tree ON e.theme.id_tree = e.tree.id
WHERE id_capa = 816
e and d are schemas. The id_capa = 816 is passed as an argument to the query from the form that I'm editing. It returns a value correctly. Now I want to edit that value on my form, so I need to UPDATE but I have multiple tables, I read that I can't do an UPDATE with JOINS, how should I do that UPDATE?
In your original SQL query, the table d.p is not used, neither in SELECT nor in conditions. So it can be skipped; the query can be rewritten as:
SELECT e.tree.nombre
FROM e.theme
LEFT JOIN e.tree ON e.theme.id_tree = e.tree.id
WHERE e.theme.id = 816
Since the query selects values from table joined with LEFT JOIN, the result can be NULL, ie joined record from e.tree table can be missed. In this case there is nothing to update.
Existing matching record can be updated with the query:
UPDATE e.tree
SET nombre = <NEW_VALUE>
FROM e.theme
WHERE e.theme.id = 816 AND e.theme.id_tree = e.tree.id

T-SQL: Joining on two separate fields based on specific criteria in a query

I have a query in which I am trying to get additional fields from another table through a join field that I manually create. The issue is when the field I create is null, then I want to use another field to join on. I am not sure how to do that without getting duplicate results. I tried a UNION query, but that just displays everything where the values are null when the manually created field value is null. Here is the query:
SELECT
BU = m.BU,
BUFBA = m.BUFBA,
a.CostCenter,
Delegate = m.Delegate,
a.DistrictLookup,
PCOwner = m.PCOwner,
a.PGr,
a.POrg,
PrimaryContact = m.PrimaryContact,
WarehouseManager = m.WarehouseManager,
Zone = m.Zone,
ZoneFBA = m.ZoneFBA
FROM
(SELECT
e.CostCenter,
e.District,
DistrictLookup =
CASE
WHEN e.PGr IN ('N01','BQE','BQA') THEN 'GSS'
WHEN e.PGr = 'BQB' THEN 'BG'
WHEN e.PGr = 'BQF' THEN 'FP'
ELSE e.District
END,
e.PGr,
e.POrg
FROM dbo.E1P e (NOLOCK)
WHERE
e.CoCd = '4433'
) a
LEFT JOIN dbo.Mapping m (NOLOCK) ON m.District = a.DistrictLookup
When the DistrictLookup field is NULL, I need a different join to occur so that the additional fields populate. That join would be:
LEFT JOIN dbo.Mapping m (NOLOCK) ON m.CostCenter = a.CostCenter
How can I write in this second join and not get duplicate results? This is a separate join on different fields and I think it differs from the other methods of doing a conditional join. If it, can someone please explain how to implement that logic into my query?
I believe this is what you are after...
LEFT JOIN dbo.Mapping m (NOLOCK)
ON (a.DistrictLookup IS NOT NULL AND m.District = a.DistrictLookup)
OR (a.DistrictLookup IS NULL AND m.CostCenter = a.CostCenter)

Multiple WHERE clause for same Columns in TSQL

I am trying to query two tables that are in 1-to-many relationship.
What I've done is create a View knowing that i might end up with multiple records for the first table.
My scenario is as follows: I have a table "Items" and table "Properties".
"Properties" table contains an ItemsId column, PropertyId, PropertyValueId columns.
"Items" table/object contains a list of "Properties".
How would I query that "View" such that, I want to get all "Items" records that have a combination of "PropertyId" & "PropertyValueId" values.
In other words something similar to:
WHERE
(PropertyId = #val1 AND PropertyValueId = #val2) OR
(PropertyId = #val3 AND PropertyValueId = #val4) OR
(PropertyId = #val5 AND PropertyValueId = #val6)
WHERE clause is just a loop over "Items.Properties" collection.
"Items" represents a table of Items being stored in the database. Each & every Item has some dynamic properties, one or more. That's why I have another table called "Properties". Properties table contains columns:
ItemId, PropertyId, PropertyValue
"Item" object has a collection of Properties/Values. Prop1:val1, Prop2:val2, etc ...
Thanks
I may not have understood your requirement (despite the update) - if this or any other answer doesn't solve the problem please add some sample data for Items, Properties and the output and then hopefully it would become clear.
If Items is a specification of the property name-value pairs that you need (and has nothing to do with ItemId on Properties which seems strange...)
select p.itemid
from properties p
where exists (select 1 from items i where i.propertyId = p.propertyId and i.propertyValueId = p.propertyValueId)
group by p.itemid
having count(distinct p.propertyid) = (select count(*) from items)
This returns a set of itemids that have one (and only one) property value for each property defined in items. You can put the items count into a variable if you want.
I would use a query like this:
SELECT ItemId
FROM ItemView
WHERE (PropertyId = #val1 AND PropertyValueId = #val2)
OR (PropertyId = #val3 AND PropertyValueId = #val4)
OR (PropertyId = #val5 AND PropertyValueId = #val6)
GROUP BY ItemId
HAVING COUNT(*) = 3
The WHERE clause is the same as in your question, it only allows a row to be selected if the row has a matching property. You only need to make sure additionally that the items obtained have all the properties in the filter, which is done in the above query with the help of the HAVING clause: you are requesting items with 3 specific properties, therefore the number of properties per item in your result set (COUNT(*)) should be equal to 3.
In a more general case, when the number of properties queried may be arbitrary, you should probably consider passing the arguments in the form of a table and join the view to it:
…
FROM ItemView v
INNER JOIN RequestedProperties r ON v.PropertyId = r.Id
AND v.PropertyValueId = r.ValueId
GROUP BY v.ItemId
HAVING COUNT(*) = (SELECT COUNT(*) FROM RequestedProperties)