简体   繁体   中英

Filter second subquery in UNION by results of first subquery

I'm writing a query to pull contact data based on whether someone is a "high donor" or "low donor", and my query works perfectly except for that it will include someone as both a highDonor and a lowDonor if they're in both categories, when we'd really want to just include them as a highDonor.

(For example: Jay Smith donated $1000 in June and $50 in July. Jay would be listed as both a highDonor and lowDonor.)

To fix this problem, I'm trying to exclude contacts from being lowDonors based on if they were already found to be highDonors. My original query unioned these subqueries, but when I try to alias the individual subqueries and reference highDonors in the second subquery, I get an error.

(Note: this is a shortened version of the full query that focuses in on the pieces that cause the error.)

This is the error I get: #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'highDonors.contactId) as lowDonors ) as highAndLowDonors ' at line 23 #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'highDonors.contactId) as lowDonors ) as highAndLowDonors ' at line 23

SELECT DISTINCT
... 
FROM
    (( 
      SELECT DISTINCT contactid, 
                      "highDonor" AS highorlowdonor 
      FROM            donation 
      WHERE           donationdate > "2015-07-03" 
      AND             donationamount BETWEEN 100 AND 9999.99)
    AS highdonors
UNION ALL 
    ( 
      SELECT DISTINCT contactid, 
                      "lowDonor" AS highorlowdonor 
      FROM            donation 
      WHERE           donationdate > "2015-07-03" 
      AND             donationamount BETWEEN 1 AND 99.99 
      WHERE           contactid NOT IN highdonors.contactid)
    AS lowdonors )
    AS highandlowdonors
    ...

Any thoughts on what's causing the error here? Can I alias two subqueries like this and use the first one inside the second?

You cannot use the result from the first query in the second unless you repeat the first query.

But you could rewrite the query to group by the contactid and get the maximum donationamount . Put your logic in a CASE looking at that maximum.

SELECT contactid,
       CASE
         WHEN max(donationamount) BETWEEN 100 AND 9999.99
           THEN 'highDonor'
         WHEN max(donationamount) BETWEEN 1 AND 99.99
           THEN 'lowDonor'
       END highorlowdonor
       FROM donation
       WHERE donationdate > '2015-07-03'
       GROUP BY contactid
       HAVING CASE
                WHEN max(donationamount) BETWEEN 100 AND 9999.99
                  THEN 'highDonor'
                WHEN max(donationamount) BETWEEN 1 AND 99.99
                  THEN 'lowDonor'
              END IS NOT NULL;

(Note: Without the HAVING this would include donors who have donated an amount < 1 or an 99.99 < amount < 100 or an amount > 9999.99 but with highorlowdonor being null. Instead of using HAVING I'd rather recommend to adapt your logic to use < (or >) rather than <= (or >=), which BETWEEN is an alias for.)

And BTW: You're using double quotes ( " ) where you should use single quotes ( ' ) (to enclose string and date literals).

You cannot alias two subqueries like this and use the first one inside the second. You can try the following:

    SELECT DISTINCT contactid, 
                      "highDonor" AS highorlowdonor 
      FROM            donation 
      WHERE           donationdate > "2015-07-03" 
      AND             donationamount BETWEEN 100 AND 9999.99)
    AS highdonors
UNION ALL 

      SELECT DISTINCT contactid, 
                      "lowDonor" AS highorlowdonor 
      FROM            donation 
      WHERE           donationdate > "2015-07-03" 
      AND             donationamount BETWEEN 1 AND 99.99 
      AND           contactid NOT IN (select contact_id
FROM            donation 
      WHERE           donationdate > "2015-07-03" 
      AND             donationamount BETWEEN 100 AND 9999.99)

A more efficient way would be store the first union select in a temp table and use that in the union and then use the same temp table in the second union select query to exclude the high donors.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM