Using EXCEPT with NOT EXISTS SELECT DISTINCT cname
FROM account AS cust_account
WHERE NOT EXISTS ( SELECT type
                   FROM account
                   EXCEPT
                   SELECT type
                   FROM account
                   WHERE account.cname = cust_account.cname )


Can someone please explain step-by-step how the query produces the result required by the question? I am just learning about NOT EXISTS and am not sure what values it takes from the parenthesis and where the WHERE clause belongs with.

This is sample table account
There are three separate parts to the query. I'll label them so I can reference each more clearly:

SELECT DISTINCT cname             -- Part A
FROM account AS cust_account
WHERE NOT EXISTS ( SELECT type    -- Part B
                   FROM account
                   EXCEPT
                   SELECT type    -- Part C
                   FROM account
                   WHERE account.cname = cust_account.cname )


First the inner query. Part C lists all type values for account rows owned by the customer currently being considered by the outer query (Part A). Conceptually you can think of the DBMS reading all rows that match the outer query then stepping through them one at a time. For each outer row it executes the inner query. (In reality likely something more time-efficient happens but it will be logically equivalent to this.)

Part C is sometimes called a correlated sub-query because it is matched (correlated) with a value coming from the outer query. In this case it is matching on cname. It is the WHERE clause that tells the DBMS to match Part C to Part A. cust_account is an alias defined in the outer query. Because a single table - account - is used three times in the statement the alias allows us to specify exactly which of these three we want to reference. It is the alias which allows us to say "match Part C to Part A."

Part B lists all type values from table account. This part has no WHERE so it lists all accounts for all users, not just the one currently being considered by the outer query. This part is not correlated.

The EXCEPT takes the results of the query above it (Part B) and removes those rows which occur in the query below it (Part C). Since Part B is "all types that are in the database" and Part C is "all types the current cname owns" then "B except C" is "all types except those which the current cname owns." If we have a list of all types, and we remove those which the cname owns, what is left is those types which the cname does not own. So if the inner query has any rows (for the current cname) we know there is at least one type of account that cname does not own.

EXISTS() returns true if the inner query has row(s). So NOT EXISTS() returns true if the inner query is empty. As we have just shown, it will be empty if the current cname owns all types of accounts. So the outer query lists all cname which have all types of account - as requested in the problem.