Issue updating in MySQL where the count of selected rows is 1 - mysql

I am having a problem updating a table with a simple update statement.
The data looks like this
+----------------------------------------------------------------------+
| id | contractor_id | domain | primary_domain |
_______________________________________________________________________|
| 1 | 50 | foo.com | 1 |
|----------------------------------------------------------------------|
| 2 | 50 | foo.net | 0 |
|----------------------------------------------------------------------|
| 3 | 100 | widget.com | 0 |
+----------------------------------------------------------------------+
What I am trying to do is update only contractors that have 1 domain and that domain is set to 0 ... So in this case it would ignore contractor 50 and set contractor 100's primary_domain to 1
This is what I've come up with, but it's failing and I am not sure why -- Can someone explain my short-coming? How do I achieve the intended result?
UPDATE domains_test SET primary_domain = '1'
WHERE(
SELECT primary_domain
FROM domains_test
WHERE primary_domain = 0
HAVING domain = 1);
EDIT
I have even tried HAVING count(domain) = 1);
The response is always
You can't specify target table 'domains_test' for update in FROM clause

To correctly get the contractor_id values related to just one domain you can use the following query:
SELECT contractor_id
FROM domains_test
GROUP BY contractor_id
HAVING COUNT(DISTINCT domain) = 1;
Output:
contractor_id
--------------
100
So, the UPDATE can be written as:
UPDATE domains_test
SET primary_domain = 1
WHERE primary_domain = 0 AND
contractor_id IN (SELECT contractor_id
FROM (
SELECT contractor_id
FROM domains_test
GROUP BY contractor_id
HAVING COUNT(DISTINCT domain) = 1) AS t);
The nesting in the subquery is necessary due to the error:
you can't specify target table 'domains_test' for update in FROM clause
This error appears in case contractor_id is obtained directly from domains_test in the subquery.
Demo here

SQL DEMO
UPDATE domains_test AS d
LEFT JOIN
(SELECT contractor_id
FROM domains_test
GROUP BY contractor_id
HAVING COUNT(*) = 1) as f
ON d.contractor_id = f.contractor_id
SET d.primary_domain = 1
WHERE d.primary_domain = 0
AND f.contractor_id IS NOT NULL;

Your logic is off-colour: You want to update domains where the COUNT of a subquery is a certain value; in this case, where a count of contractors with only 1 domain and that domain is of 0 value.
you currently do not count anything in your SQL. Instead use this:
UPDATE domains_test SET primary_domain = 1
WHERE (
SELECT COUNT(*) FROM domains_test
GROUP BY (contractor_id)
) = 1
AND primary_domain = 0
What this does: First it searches for groups of contractors where the contractor_id occurs only once in the count, and then for each on of these it checks the value of primary_domain, updating as appropriate when both conditionals are true.

Related

Problems using SQL ALL operator

I'm having trouble using/understanding the SQL ALL operator. I have a table FOLDER_PERMISSION with the following columns:
+----+-----------+---------+----------+
| ID | FOLDER_ID | USER_ID | CAN_READ |
+----+-----------+---------+----------+
| 1 | 34353 | 45453 | 0 |
| 2 | 46374 | 342532 | 1 |
| 3 | 46374 | 32352 | 1 |
+----+-----------+---------+----------+
I want to select the folders where all the users have permission to read, how could I do it?
Use aggregation and having:
select folder_id
from t
group by folder_id
having min(can_read) = 1;
Gordon's answer seems better but for the sake of completeness, using ALL a query could look like:
SELECT x1.folder_id
FROM (SELECT DISTINCT
fp1.folder_id
FROM folder_permission fp1) x1
WHERE 1 = ALL (SELECT fp2.can_read
FROM folder_permission fp2
WHERE fp2.folder_id = x1.folder_id);
If you have a table for the folders themselves replace the derived table (aliased x1) with it.
But this only respects users present in folder_permissions. If not all users have a reference in that table you possibly won't get the folders really all users can read.
You can do aggregation :
SELECT fp.FOLDER_ID
FROM folder_permission fp
GROUP BY fp.FOLDER_ID
HAVING SUM( can_read = 0 ) = 0;
You can also express it :
SELECT fp.FOLDER_ID
FROM folder_permission fp
GROUP BY fp.FOLDER_ID
HAVING MIN(CAN_READ) = MAX(CAN_READ) AND MIN(CAN_READ) = 1;
If you wanted to return the full matching records, you could try using some exists logic:
SELECT ID, FOLDER_ID, USER_ID, CAN_READ
FROM yourTable t1
WHERE NOT EXISTS (SELECT 1 FROM yourTable t2
WHERE t2.FOLDER_ID = t1.FOLDER_ID AND t2.CAN_READ = 0);
Demo
The existence of a matching record in the above exists subquery would imply that there exist one or more users for that folder who do not have read access rights.

getting data from multiple tables and applying arithmatic operation on the result

I want to fetch data from two table and apply arithmetic operation on the column.
This is wha I tried :
String sql = "SELECT SUM(S.san_recover-C.amount) as total
FROM sanction S
LEFT JOIN collection C ON S.client_id = C.client_id
WHERE S.client_id=?";
This code is working only when there is value in both tables, but if there is no value in one of two tables there is no result.
SELECT SUM(S.san_recover - C.amount) as total
FROM sanction S
LEFT JOIN collection C ON S.client_id = C.client_id
WHERE S.client_id = ?
The problem with your query lies in the SUM() function. When the left join does not bring back records, then c.amount is NULL. When substracting NULL from something, you get a NULL result, which then propagates across the computation, and you end up with a NULL result for the SUM().
You probably want COALESCE(), like so:
SELECT SUM(S.san_recover - COALESCE(C.amount, 0)) as total
FROM sanction S
LEFT JOIN collection C ON S.client_id = C.client_id
WHERE S.client_id = ?
Where there is a possibility that a client may exist in one table but no another a full join would be appropriate but since mysql does not have such a thing then a union in a sub query will do
drop table if exists sanctions,collections;
create table sanctions(client_id int, amount int);
create table collections(client_id int, amount int);
insert into sanctions values
(1,10),(1,10),(2,10);
insert into collections values
(1,5),(3,10);
Select sum(Samount - camount)
From
(Select sum(amount) Samount, 0 as camount from sanctions where client_id =3
Union all
Select 0,sum(amount) as camount from collections where client_id =3
) s
;
+------------------------+
| sum(Samount - camount) |
+------------------------+
| -10 |
+------------------------+
1 row in set (0.00 sec)
If you want to do this for all clients
Select client_id,sum(Samount - camount) net
From
(Select client_id,sum(amount) Samount, 0 as camount from sanctions group by client_id
Union all
Select client_id,0,sum(amount) as camount from collections group by client_id
) s
group by client_id
;
+-----------+------+
| client_id | net |
+-----------+------+
| 1 | 15 |
| 2 | 10 |
| 3 | -10 |
+-----------+------+
3 rows in set (0.00 sec)

mysql - update distinct row in one table

I have this table:
it produces by this query SELECT DISTINCT code, tariff_diff FROM mytable
.
Now, I want to update tariff_diff = 1 if code appear more than 1. (as example, I want to update tariff_diff = 1 where row Kuta,DPS50xxx)
I have tried :
update mytable SET tariff_diff = 1
WHERE in(select distinct code, tariff_diff from mytable)
But i am getting error syntax.
Operand should contain 1 column
If you want to alter the all the rows with same code you can use this.
UPDATE mytable SET mytable.tariff_diff = 1 WHERE mytable.code IN(SELECT count(*), code, tariff_diff from mytable GROUP BY code HAVING count(*)>1)
It is not possible to use same update table in select statement in subquery , you can find the reason in this link: Reason for not use same table in sub-query.
try below query:
SET #r_code = (select code from mytable GROUP BY code having count(code) > 1);
update mytable SET tariff_diff = 1 WHERE code in (#r_code);
You can find more about variable here in this link.More about Variables.
First of all store the id's into the some variable and then update those id's using in query.
From what I understand, you're wanting to set the tariff_diff to 1 only if more than one of the rows that are prefixed with Kuta,DPS50. exist. Matching on Kuta,DPS50.06, Kuta,DPS50.07, Kuta,DPS50.08, Kuta,DPS50.09, Kuta,DPS50.10.
Assuming all of your records are formatted like: XXX,xxx.###. You can use SUBSTRING_INDEX to parse the prefixed text (Kuta,DPS50.) to use as an identifier.
Then you can use a derived JOIN to match the codes that have duplicates of the prefixed values and update the matching rows.
If there are no duplicate values, no update will occur.
Example: http://sqlfiddle.com/#!9/658034/1 (I added an additional entry for Petang,DPS50.02 to demonstrate it works on other prefixed values.)
Query:
UPDATE mytable AS p
JOIN (
SELECT SUBSTRING_INDEX(code, '.', 1) AS prefix_code
FROM mytable
GROUP BY prefix_code
HAVING COUNT(prefix_code) > 1
) AS c
ON c.prefix_code = SUBSTRING_INDEX(p.code, '.', 1)
SET p.tariff_diff = 1;
Result:
| code | tariff_diff |
|-----------------------|-------------|
| Abiansemal,DPS50.02 | 0 |
| Kuta,DPS50.06 | 1 |
| Kuta,DPS50.07 | 1 |
| Kuta,DPS50.08 | 1 |
| Kuta,DPS50.09 | 1 |
| Kuta,DPS50.10 | 1 |
| Kuta Selatan,DPS50.05 | 0 |
| Kuta Ultara,DPS50.04 | 0 |
| Mengwi,DPS50.01 | 0 |
| Petang,DPS50.02 | 1 |
| Petang,DPS50.03 | 1 |
This will also avoid the SQL Error (1093) https://dev.mysql.com/doc/refman/5.6/en/update.html
You cannot update a table and select from the same table in a subquery.

Update table only if count of rows of the same table is lower than a certain value

I have a table of people who have to confirm their assistance to an event. This event has limited capacity. I want to calculate the number of confirmed people and update the confirmation status within the same atomic query.
Let's say that PEOPLE table has id and confirmed fields.
I have tried something like this:
UPDATE PEOPLE
SET confirmed = 'Y'
WHERE
id = 'ID_VALUE'
AND
(
SELECT amount
FROM
(
SELECT
COUNT(*) AS amount
FROM
PEOPLE AS p
WHERE
p.confirmed = 'Y'
) AS aux1
) < CAPACITY_LIMIT
Example of table:
-------------------
| id | confirmed |
-------------------
| 1 | N |
-------------------
| 2 | N |
-------------------
| ... | ... |
-------------------
| 10 | N |
-------------------
If CAPACITY_LIMIT = 5 and I try to confirm sequentially from id = 1 to id = 10, I want to perform the update (confirm = 'Y') until id = 5.
I'm sure that I'm missing something, but I can't figure what is it.
Thanks in advance. Regards.
Try this:
UPDATE
PEOPLE AS p1
INNER JOIN
(SELECT COUNT(*) AS numConfirmed FROM PEOPLE AS p2 WHERE p2.confirmed = 'Y') aux
SET
p1.confirmed = 'Y'
WHERE
p1.id = 'ID_VALUE'
AND aux.numConfirmed < CAPACITY_LIMIT;
try this
SET #ct := (SELECT
COUNT(*) AS amount
FROM
PEOPLE AS p
WHERE
p.confirmed = 'Y');
UPDATE PEOPLE
SET confirmed = 'Y'
WHERE
id = '1'
AND CAPACITY_LIMIT < #ct
;
falvarez,
You might want to verify that your inputs('ID_VALUE',CAPACITY_LIMIT) are correct, including data type and actual value, since the same query works for me with no problem. The following is the query I run against my test table (as you can see the query skeleton is exactly same as yours):
update t_order set customer ='NewCustomerName' where id = 124 and
(select amount from (select count(*) as amount from t_order where customer is not null) as total) < 10

Subcount's as fields in a mysql query

For a statistics application, with a table structure as such:
unique_id | browser_family | os | date_time | js_enabled | flash_enabled | non_binary_field
1 firefox w7 ... 1 0 yes
2 chrome w7 ... 1 1 no
3 ie9 wx ... 0 0 yes
So, I'd like to perform a query with where clauses on any fields, and have it give me counts of js_enabled=1, flash_enabled =0, non_binary_field = 'yes' for those criteria (say `os` = 'w7' and date(`date_time`) = '01-08-2012').
The result would be:
count(js_enabled=1) | count(flash_enabled=1) | count(non_binary_field='yes')
2 1 1
Is this possible in a single query?
Thanks!
select sum(js_enabled=1),
sum(flash_enabled=1),
sum(non_binary_field='yes')
from your_table
where `os` = 'w7'
and date(`date_time`) = '2012-08-01'
Each field can be filled by a separate subquery:
select
(select count(js_enabled) from yourtable where js_enabled=1),
(select count(flash_enabled) from yourtable where flash_enabled=1),
(select count(non_binary_field) from yourtable where non_binary_field='yes')