Get all applications where count of positions more then 1 - mysql

I have 2 tables
LoanApplications (Id, Name, CreationDate, LoanApplicationStatusId)
Positions(Id, Name, CreationDate, LoanApplicationId)
I need to find all loan applications that have more than 1 position and update LoanApplicationStatusId to 2
I write code to get these LoanApplications like this
SELECT e.Id, count(Name) FROM LoanApplications e
INNER JOIN Positions d ON e.Id=d.LoanApplicationId
GROUP BY e.Id
HAVING COUNT(Name)>1
But I don't understand how to make an update now.
Can you help me?

Straight ahead would be a simple subselect
UPDATE LoanApplications l
SET LoanApplicationStatusId = 2
where (select count(1) from Positions p where p.LoanApplicationId = l.id) > 1

Simply select id of apps which have more than one row, and use it in UPDATE as a condition
UPDATE LoanApplications
JOIN ( SELECT LoanApplicationId
FROM Positions
GROUP BY LoanApplicationId
HAVING COUNT(LoanApplicationId) > 1 ) multi_positional ON id = LoanApplicationId
SET LoanApplicationStatusId = 2
Unsafe query: 'Update' statement without 'where' updates all table rows at once Got this stuff – Eugene Sukh
Convert this query to
UPDATE LoanApplications
JOIN ( SELECT LoanApplicationId
FROM Positions
GROUP BY LoanApplicationId
HAVING COUNT(LoanApplicationId) > 1 ) multi_positional
SET LoanApplicationStatusId = 2
WHERE LoanApplications.id = multi_positional.LoanApplicationId

Related

How to determine equaivalent groupings (recursively) in SQL?

I have a list of products identified by their SKUs. To simplify it, I just name them as A, B, C, D,... here. Each of these SKUs has been assigned by default an already existing GroupID, for simplicity I just number them as 1, 2, 3,... here.
The same GroupID would mean "These SKUs are equivalent, so it is ok to use/buy either one of them, as it makes no difference".
The problem is, some SKUs show up more than once as they come from a different buying source, but as they come from a different source, they have a different grouping.
The goal is therefore to consolidate the grouping and make sure they have the same groupings.
I already apologize if my illustration may not be super pretty, but I'm trying. Here's a small data table sample on how the raw data looks like (first line is the column names):
Source SKU GroupID
Seller1 A 1
Seller1 B 1
Seller1 C 1
Seller2 B 2
Seller2 D 2
Seller2 E 2
Seller3 A 3
Seller3 B 3
Seller4 F 4
Seller4 G 4
Seller4 H 4
The result should be like:
Source SKU GroupID
Seller1 A 1
Seller1 B 1
Seller1 C 1
Seller2 B 1
Seller2 D 1
Seller2 E 1
Seller3 A 1
Seller3 B 1
Seller4 F 4
Seller4 G 4
Seller4 H 4
Basically, if Any SKU in GroupID X is a subset of GroupID Y, then GroupID Y = GroupID X. But that should be applied on all GroupIDs, so it appears to be recursive.
I wish I could show the code that I tried already and I tried already for a few days, but I literally only managed to produce garbage.
In C# I'd know how to deal with this, but I can't seem to wrap my head around SQL as I am not that experienced and unfortunately I would need this in SQL.
I would be thankful for any kind of help, even if it's just a hint or direction you guys would suggest I should try. Thanks a lot!
You want a correspondence between groups, which you can calculate with a recursive CTE:
with recursive tt as (
select distinct t1.groupid as groupid1, t2.groupid as groupid2
from t t1 join
t t2
on t1.sku = t2.sku
),
cte as (
select tt.groupid1, tt.groupid2, concat_ws(',', tt.groupid1, tt.groupid2) as visited
from tt
union all
select cte.groupid1, tt.groupid2, concat_ws(',', visited, tt.groupid2)
from cte join
tt
on cte.groupid2 = tt.groupid1
where find_in_set(tt.groupid2, cte.visited) = 0
)
select groupid1, min(groupid2) as overall_group
from cte
group by groupid1;
You can then join this back to the original table to get the "overall group":
with recursive tt as (
select distinct t1.groupid as groupid1, t2.groupid as groupid2
from t t1 join
t t2
on t1.sku = t2.sku
),
cte as (
select tt.groupid1, tt.groupid2, concat_ws(',', tt.groupid1, tt.groupid2) as visited
from tt
union all
select cte.groupid1, tt.groupid2, concat_ws(',', visited, tt.groupid2)
from cte join
tt
on cte.groupid2 = tt.groupid1
where find_in_set(tt.groupid2, cte.visited) = 0
)
select t.*, g.overall_group
from t join
(select groupid1, min(groupid2) as overall_group
from cte
group by groupid1
) g
on t.groupid = g.groupid1;
Here is a db<>fiddle.
Note: Your sample data is rather "complete" so you don't need a recursive CTE for that particular data. However, I am guessing that your real groups have a bit less overlap in which case recursion is necessary.
First is to get all those sellers with subsets based on count. then filter using Group By
select table1.Source, SKU, case when table1.Source = t6.Source and t6.cnt > 1 then 1 else 2 end as GroupID
from table1
left join
(select t5.Source, count(t5.cnt) as cnt from (
select distinct t4.Source, t4.cnt from (
select t3.Source, count(t3.SKU) as cnt from (
select t1.Source, t1.SKU from table1 t1
left join table1 t2 on t2.SKU = t1.SKU ) t3
group by t3.Source, t3.SKU
order by t3.Source) t4) as t5
group by t5.Source) t6 on t6.Source = table1.Source

mysql query group by totals

I am using the following query to retrieve the number of events per state from 2 tables that are linked by a userID.
SELECT state,COUNT(*) AS num
FROM tableUserInfo
WHERE userID IN (SELECT userID
FROM tableEvents
WHERE conditionOne = 1
AND conditionTwo = 2)
GROUP BY state
This query works correctly. My problem is that not all states have user entries, and I need the query to return 0 for those. I was wondering if there was a method such as joining or using an in clause, that would included a set of all states, making the query return 0 for any that didn't have entries in tableEvents?
Do you have a list of states? If not then this would give a list of all the states your database knows about:
SELECT DISTINCT state FROM tableUserInfo
....and enclosing this in brackets it can be dropped in place in the query below:
SELECT s.state, IFNULL(cnt, 0) AS num
FROM list_of_states s
LEFT JOIN (
SELECT state,COUNT(*) AS cnt
FROM tableUserInfo ui
INNER JOIN tableEvents te
ON ui.userId=te.userId
WHERE conditionOne = 1
AND conditionTwo = 2
GROUP BY state
) u
ON s.state=u.state;
Although in the absence of "list_of_states" it would be more efficient to do this:
SELECT ui.state, SUM(IF(te.userId IS NULL, 0, 1)) AS cnt
FROM tableUserInfo ui
LEFT JOIN tableEvents te
ON ui.userId=te.userId
AND te.conditionOne = 1
AND te.conditionTwo = 2
GROUP BY state;
As #raymond-nijland suggested you can use Left Join to include all states.
SELECT tableUserInfo.state,COUNT(tableUserInfo.*) AS num
FROM tableUserInfo Left Join tableEvents on tableUserInfo.userID = tableEvents.userID
WHERE tableEvents.conditionOne = 1 AND tableEvents.conditionTwo = 2
GROUP BY state

MySQL combine UPDATE and SELECT query

I have the following SELECT statement that returns data, example below:
SELECT performers.ID,
performers.Name,
COUNT(*) AS CountOfDeals,
COUNT(DISTINCT(deals.Name)) AS CountOfAliases
FROM deals RIGHT JOIN performers
ON deals.name LIKE CONCAT('%', performers.name, '%')
WHERE performers.ID IN ( 27952, 27951, 27950, 27949, 27948 )
GROUP BY Name;
Example data returned:
ID Name CountOfDeals CountOfAliases
27952 Christine Hoberg 1 0
27951 Indian Jewelry 1 0
27952 Kinky Friedman 5 3
27949 KJ-52 1 0
27960 River Whyless 1 0
I want to combine this with the following UPDATE statement
UPDATE performers
SET RawAliasCount = CountOfAliases,
RawDealCount = CountOfDeals
WHERE ID = ?
All the values needed to run the update statement are returned in the select statement above so hopefully this should be pretty easy.
Thanks.
Use update with join:
UPDATE performers p JOIN
(SELECT performers.ID, performers.Name, COUNT(*) AS CountOfDeals,
COUNT(DISTINCT(deals.Name)) AS CountOfAliases
FROM deals RIGHT JOIN
performers
on deals.name LIKE CONCAT('%', performers.name, '%')
WHERE performers.ID IN (27952, 27951, 27950, 27949, 27948)
GROUP BY Name
) pp
ON pp.id = p.id
SET RawAliasCount = pp.CountOfAliases,
RawDealCount = pp.CountOfDeals;
UPDATE performers
SET performers.RawAliasCount = count_table.CountOfAliases, performers.RawDealCount = count_table.CountOfDeals
FROM performers
INNER JOIN
(
SELECT
performers.ID, performers.Name, COUNT(*) AS CountOfDeals,
COUNT(DISTINCT(deals.Name)) AS CountOfAliases
FROM deals RIGHT JOIN performers on deals.name LIKE CONCAT('%', performers.name, '%')
WHERE performers.ID IN (27952, 27951, 27950, 27949, 27948)
GROUP BY Name
) count_table
ON count_table.ID = performers.ID;
When this type of question is asked, thank you to put the tables schema.
edit : sorry, it's sql-server syntax.

MySQL select in update statement

This MySQL statement give me all id_duel_player for player with id_player=30 and it work fine.
SELECT b.id_duel_player
FROM duels a
INNER JOIN duel_player b
ON a.id_duel = b.id_duel
WHERE id_player = 30
UNION ALL
SELECT c.id_duel_player
FROM duel_player c
INNER JOIN
(
SELECT aa.*
FROM duels aa
INNER JOIN duel_player bb
ON aa.id_duel = bb.id_duel
WHERE bb.id_player = 30
) d ON c.id_duel = d.id_duel AND c.id_player <> 30
I want to make MySQL statement for UPDATE (fields from duel_player table) all of this id_duel_player that returns this select statement.
UPDATE duel_player
SET num = 2,
total = 5
WHERE (duel_player.id_duel_player = id_duel_player's from above SELECT statement)
I want most effective and fastest way to do this.
Thanks
For 200-400 rows it's likely fastest to create a temporary table with the results, and then do the UPDATE with a join:
CREATE TEMPORARY TABLE id_duel_players AS
SELECT b.id_duel_player as id FROM duels a ...
UPDATE duel_player
JOIN id_duel_players ON duel_player.id_duel_player = id_duel_players.id
SET num = 2,
total = 5
For smaller result sets you may find the IN operator sufficiently fast (... WHERE id_duel_player IN (SELECT ...)), but I've found it unreliable for result sets with hundreds of rows. (Unreliable = suddenly no matches are found, no idea why, I haven't investigated.)

Is there a way to optimize this update query?

I have a master table called "parent" and a related table called "childs"
Now I run a query against the master table to update some values with the sum from the child table like this.
UPDATE master m SET
quantity1 = (SELECT SUM(quantity1) FROM childs c WHERE c.master_id = m.id),
quantity2 = (SELECT SUM(quantity2) FROM childs c WHERE c.master_id = m.id),
count = (SELECT COUNT(*) FROM childs c WHERE c.master_id = m.id)
WHERE master_id = 666;
Which works as expected but is not a good style because I basically make multiple SELECT querys on the same result. Is there a way to optimize that? (Making a query first and storing the values is not an option.
I tried this:
UPDATE master m SET (quantity1, quantity2, count) = (
SELECT SUM(quantity1), SUM(quantity2), COUNT(*)
FROM childs c WHERE c.master_id = m.id
) WHERE master_id = 666;
but that doesn't work.
Update: Here is the solution, thanks to everbody:
You can do something like this:
UPDATE master m
INNER JOIN childs c ON m.master_id = c.master_id
SET master.quantity1 = c.quantity1,
master.count = 1
If you have only one child record at a time. However if you want to use a group function like SUM() in the joined table that doesn't work. Either you get a "Invalid use of group function" if you leave the "group by" part or a "You have an error in your sql syntax if you use "GROUP BY c.master_id"
-- This doesnt work :(
UPDATE master m
INNER JOIN childs c ON m.master_id = c.master_id
SET master.quantity1 = SUM(c.quantity1),
master.count = COUNT(c.*)
GROUP by c.master_id
The solution is to use JOIN with a subquery:
UPDATE master m
INNER JOIN
(
SELECT master_id,
SUM(quantity1) as quantity1,
COUNT(*) as count
FROM childs c
GROUP BY master_id
) c
ON c.master_id = m.master_id
SET m.quantity1 = c.quantity1,
m.count = c.count
WHERE m.master_id = 666;
But since this pulls every row from the childtable the overhead would likely be bigger than using more subqueries like in the original sql. So you should add a WHERE clause to the joined table to get only the rows you need.
Another interesting approach is this syntax, which does the same as the JOIN with the WHERE clause but you should only use if if you want to update all rows with the same values and your subquery only returns one row, since the result from the subquery gets appended to the result and can be used like any column.
UPDATE master m,
(
SELECT SUM(c.quantity1) as sum_of_quantity,
COUNT(*) as rowcount FROM child c WHERE c.master_id = 666
) as c
SET m.quantity1 = c.sum_of_quantity,
m.count = c.rowcount
WHERE m.master_id = 666;
Rewriting Lieven's solution to MySQL:
UPDATE master m
JOIN (
SELECT master_id
, SUM(quantity1) as quantity1
, SUM(quantity2) as quantity2
, COUNT(*) as count
FROM childs c
GROUP BY
master_id
) c
ON c.master_id = m.master_id
SET
m.quantity1 = c.quantity1
,m.quantity2 = c.quantity2
,m.count = c.count
WHERE m.master_id = 666;
I don't know if it is allowed in MySQL, but SQL Server allows you to use the result of a select in an update.
UPDATE master m SET
quantity1 = c.quantity1
, quantity2 = c.quantity2
, count = c.count
FROM master m
INNER JOIN (
SELECT master_id
, quantity1 = SUM(quantity1)
, quantity2 = SUM(quantity2)
, count = COUNT(*)
FROM childs c
WHERE master_id = 666
GROUP BY
master_id
) c ON c.master_id = m.master_id
You could select your data into a temporary table, and then update using that data.
If you also want to insert "new" data in the same roundtrip, look into INSERT INTO ... SELECT FROM ... ON DUPLICATE KEY UPDATE ...
If you already are doing inserts if row doesn't exist, then that would be redundant with this example.
example:
INSERT INTO master m (id, quantity1, quantity2, count)
SELECT master_id, SUM(quantity1) q1, SUM(quantity2) q1, COUNT(*) c
FROM childs
GROUP BY master_id
ON DUPLICATE KEY UPDATE
m.quantity1 = q1,
m.quantity2 = q2,
m.count = c
NOTE! This is untested code, but I think it should be possible to backreference the select result in the UPDATE.
Syntax reference: http://dev.mysql.com/doc/refman/5.0/en/insert.html