N1QL update document with subquery - couchbase

I'm trying to denormalise a "Country" document with "regionName" field corresponding to "name" of another "Region" document. My N1QL query is as below but it's not working
update test as t1 set regionName = (
select raw name from test as t2 where `_class`="Region"
) where `_class`="Country" and t1.regionCode=t2.code RETURNING *;
Any help?

N1QL doesn't have UPDATE JOINS. You can't USE SET CLAUSE subquery source in UPDATE WHERE clause.
Use Merge
MERGE test AS m
USING test AS s
ON s.`_class`="Region" AND m.`_class`="Country" AND m.regionCode=s.code
WHEN MATCHED THEN m.regionName = s.name;
https://blog.couchbase.com/ansi-join-enhancements-and-ansi-merge/
CB 6.0
MERGE test AS m
USING (SELECT META(c).id, r.name
FROM test AS r
JOIN test AS c
ON r.`_class`="Region" AND c.`_class`="Country"
AND r.regionCode = c.code) AS s
ON KEY s.id
WHEN MATCHED THEN m.regionName = s.name;

Related

Error Code: 1093. You can't specify target table 't' for update in FROM clause

update test as t
SET t.agent=86
where t.id in (select tgt.test_id from test_group_tests as tgt where tgt.test_id in
(select t.id from test where t.agent in (1)) and tgt.testgroup_id not in (20,58,23,37,447));
I tried this query and I see this error. Is it missing inner join or something?
In general, MySQL won't allow you to SELECT a table and UPDATE the same table in the same query. You can, however, write a multi-table UPDATE by joining tables.
The way I read your query, it seems like the following would do the same thing:
UPDATE test AS t
JOIN test_group_test AS tgt ON (t.id=tgt.test_id)
JOIN test AS t2 ON (tgt.test_id=t2.id)
SET t.agent=86
WHERE t2.agent IN (1)
AND tgt.testgroup_id NOT IN (20,58,23,37,447);
Though I have not tested this query. It's meant as an example of using JOIN for a multi-table UPDATE. So test it yourself carefully to see if it does what you intend, and keep a backup of your original data in case it doesn't.
See https://dev.mysql.com/doc/refman/5.7/en/update.html for more on multi-table UPDATE in MySQL.
In the code below, the subquery will return all test id's not in the groups (20,58,23,37,447), but the update will only update the test records where agent is in (1).
update test as t
SET t.agent=86
where t.agent in (1)
and t.id in
(
select
tgt.test_id
from test_group_tests as tgt
where tgt.testgroup_id not in (20,58,23,37,447)
);

MySQL Conditional Update in same table

I'm looking for a simple way to do an update on a table only if there is no other columns present in that same table with the same value I'm trying to update, ideally in a single query. So far I'm getting an error You specify target table 't1' for update in FROM clause. Here is what I tried in a few variations so far (still unable to get working):
UPDATE emailQueue AS t1
SET
t1.lockedOn = 1470053240
WHERE
(SELECT
COUNT(*)
FROM
emailQueue AS t2
WHERE
t2.lockedOn = 1470053240) = 0
AND t1.lockedOn IS NULL
In MySQL, you need to use a join. In this case, a left join is in order:
UPDATE emailQueue eq LEFT JOIN
emailQueue eq2
ON eq2.lockedOn = 1470053240
SET eq.lockedOn = 1470053240
WHERE eq.lockedOn IS NULL AND
eq2.lockedOn IS NULL;

Differentiating Between Table Names

I currently have two tables that have the same column names;
APPLICATION_Reference.firstName, APPLICATION_Reference.lastName
APPLICATION_Recommendation.firstName, APPLICATION_Recommendation.lastName
$all_references = mysqli_query($con, "SELECT *
FROM APPLICATION_Reference
INNER JOIN APPLICATION_Recommendation
ON APPLICATION_Reference.referenceId = APPLICATION_Recommendation.referenceId
INNER JOIN Applicant ON APPLICATION_Reference.firstName = Applicant.givenName
ORDER BY APPLICATION_Reference.referenceId ASC");
And then I have an echo statement where I echo out my data: $applicant_reference['firstName']
My question is: How do I differentiate between my different tables when echoing out the column names?
Use table aliases and column aliases.
In your query you can alias a whole table to make things more readable, e.g.
APPLICATION_Reference ref
Your query then becomes
SELECT *
FROM APPLICATION_Reference ref
INNER JOIN APPLICATION_Recommendation rec ref.referenceId = rec.referenceId
INNER JOIN Applicant app ON ref.firstName = app.givenName
ORDER BY ref.referenceId ASC
Instead of then using SELECT *, list the fields you are interested in and rename them to something that makes sense in your result set, e.g.
SELECT ref.firstName reference_first_name,
rec.firstname recommendation_first_name,
...
You can then access the new column names in your PHP code. You certainly don't have to use such long references (e.g. reference_first_name) - those are just examples.
you could use
SELECT *, table1.field AS field1, table2.field AS field2 ...
and then reference the fields as field1 and field2 in your output
You can use aliases
SELECT
APPLICATION_Recommendation.lastName as APPLICATION_Recommendation_lastName,
Applicant .lastName as Applicant _lastName
...
FROM APPLICATION_Reference
INNER JOIN APPLICATION_Recommendation ON APPLICATION_Reference.referenceId = APPLICATION_Recommendation.referenceId
INNER JOIN Applicant ON APPLICATION_Reference.firstName = Applicant.givenName
ORDER BY APPLICATION_Reference.referenceId ASC

Loop through column and update it with MySQL?

I want to loop through some records and update them with an ad hoc query in MySql. I have a name field, so I just want to loop though all of them and append a counter to each name, so it will be name1, name2, name3. Most examples I see use stored procs, but I don't need a stored proc.
As a stepping stone on your way to developing an UPDATE statement, first generate a SELECT statement that generates the new name values to your liking. For example:
SELECT t.id
, t.name
, CONCAT(t.name,s.seq) AS new_name
FROM ( SELECT #i := #i + 1 AS seq
, m.id
FROM mytable m
JOIN (SELECT #i := 0) i
ORDER BY m.id
) s
JOIN mytable t
ON t.id = s.id
ORDER BY t.id
To unpack that a bit... the #i is a MySQL user variable. We use an inline view (aliased as i) to initialize #i to a value of 0. This inline view is joined to the table to be updated, and each row gets assigned an ascending integer value (aliased as seq) 1,2,3...
We also retrieve a primary (or unique) key value, so that we can match each of the rows from the inline view (one-to-one) to the table to be updated.
It's important that you understand how that statement is working, before you attempt writing an UPDATE statement following the same pattern.
We can now use that SELECT statement as an inline view in an UPDATE statement, for example:
UPDATE ( SELECT t.id
, t.name
, CONCAT(t.name,s.seq) AS new_name
FROM ( SELECT #i := #i + 1 AS seq
, m.id
FROM mytable m
JOIN (SELECT #i := 0) i
ORDER BY m.id
) s
JOIN mytable t
ON t.id = s.id
ORDER BY t.id
) r
JOIN mytable u
ON u.id = r.id
SET u.name = r.new_name
SQL Fiddle demonstration here:
http://sqlfiddle.com/#!2/a8796/1
I had to extrapolate, and provide a table name (mytable) and a column name for a primary key column (id).
In the SQL Fiddle, there's a second table, named prodtable which is identical to mytable. SQL Fiddle only allows SELECT in the query pane, so in order to demonstrate BOTH the SELECT and the UPDATE, I needed two identical tables.
CAVEAT: be VERY careful in using MySQL user variables. I typically use them only in SELECT statements, where the behavior is very consistent, with careful coding. With DML statements, it gets more dicey. The behavior may not be as consistent in DML, the "trick" is to use a SELECT statement as an inline view. MySQL (v5.1 and v5.5) will process the query for the inline view and materialize the resultset as a temporary MyISAM table.
I have successfully used this technique to assign values in an UPDATE statement. But (IMPORTANT NOTE) the MySQL documentation does NOT specify that this usage or MySQL user variables is supported, or guaranteed, or that this behavior will not change in a future release.
Have the names stored in a table. Do a join against the names and update in the second table you want to.
Thanks

Issue with the TOP 1 query

Is it possible to achieve next thing without using views, but just one single query? I have two tables:
TableA->TanbleB (1-many) ON TableA.Id = TableB.TableAId
I need to update one field in Table A (TableA.Field1) for records in TableA that satisfy condition on one field in tableA (WHERE TableA.Field2=SomeValue)
.
TableA.Field1 will be updated from TableB with value that is last inserted (last inserted value in related records to TableA).
I will put an example:
UPDATE TableA a SET Field1 = (SELECT TOP 1 b.Feild1 * b.Field2 FROM TableB b WHERE b.TableAId = a.id) WHERE field2 = 1
I know Above example doesn't work, but I have many ways tried using INNER JOIN and failed. I had an idea to use something like this:
UPDATE TableA INNDER JOIN ( SELECT ... FROM TABLE B) ON TABLEA.Id= TableB.TableAId SET ....
But the 2ns query should return 1 record for each DISTINCT TableAId, but only the last inserted.
I hope I am making some sense here.
Thanks in advance.
Here is some SQL that will do what you want
UPDATE T1 INNER JOIN T2 ON T1.ID = T2.T1ID SET T1.F2 = [T2].[F2]*[T2].[F3] WHERE (((T1.F1)="ABC") AND ((T2.ID)=DMax("[ID]","[T2]","[T1ID]=" & [T1].[ID])));
This predicated on T1.ID being the primary key for T1 and T2.T1ID being a index field in T2
One of the flaws in Access is that you can't run an "UPDATE" query based on a "SELECT" query, it will usually give the error:
Operation must use an updateable query
The only way around is as you say to create a view of the "SELECT" query and then inner join this on your table, Access is then working with a static recordset and can handle the "UPDATE" query ok
Alternatively you could write a VBA procedure to step through line by line with the Recordset.
Best of luck : )
UPDATE:
SELECT b.TableAId, b.Feild1 * b.Field2 INTO tblView FROM TableB As b WHERE b.field2 = 1