A managed to cobble together SQL that should work for your example based on the small data set in your example.
As I mentioned in my earlier comment the stored procedure splits the artist string and inserts into a temp table. From there it's a simple count and group query like you have above.
After creating the function and procedures you should be able to execute the stored procedure listArtistCounts to get your results
My SO sources are listed just above the code.
SPLIT_STR function
source
delimiter $$
CREATE FUNCTION SPLIT_STR(
x VARCHAR(255),
delim VARCHAR(12),
pos INT
)
RETURNS VARCHAR(255)
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
delim, '');
$$
AddSplitRecords procedure
delimiter $$
CREATE PROCEDURE AddSplitRecords(fullstr)
BEGIN
DECLARE a INT Default 0 ;
DECLARE str VARCHAR(255);
simple_loop: LOOP
SET a=a+1;
-- Split based on " & "
SET str=SPLIT_STR(fullstr," & ",a);
IF str='' THEN
LEAVE simple_loop;
END IF;
#Do Inserts into temp table here with str going into the row
insert into temp_table values (str);
END LOOP simple_loop;
END $$
listArtistCounts Procedure
source
create procedure listArtistCounts()
begin
-- Variable to hold artist field from query
declare cArtistList varchar(255);
-- Variables related to cursor:
-- 1. 'done' will be used to check if all the rows in the cursor
-- have been read
-- 2. 'curArtist' will be the cursor: it will fetch each row
-- 3. The 'continue' handler will update the 'done' variable
declare done int default false;
declare curArtist cursor for
SELECT artist FROM list GROUP BY artist, title; -- This is the query used by the cursor.
declare continue handler for not found -- This handler will be executed if no row is found in the cursor (for example, if all rows have been read).
set done = true;
CREATE TEMPORARY TABLE IF NOT EXISTS temp_table(artist varchar(255) NOT NULL);
-- Open the cursor: This will put the cursor on the first row of its rowset.
open curArtist;
-- Begin the loop (that 'loop_artist' is a label for the loop)
loop_artist: loop
-- When you fetch a row from the cursor, the data from the current
-- row is read into the variables, and the cursor advances to the
-- next row.
-- If there's no next row, the 'continue handler for not found'
-- will set the 'done' variable to 'TRUE'
fetch curArtist into cArtistList;
-- Exit the loop if you're done
if done then
leave loop_artist;
end if;
-- Execute insert from row.
AddSplitRecords cArtistList
end loop;
-- Close the cursor when done
close curArtist;
-- Output results of distinct artists counted
SELECT artist, COUNT(*) AS total FROM temp_table GROUP BY artist ORDER BY total DESC
end $$
delimiter ;