Question

I have sql query where i like to get total records.

I have one employer and 600 jobs by this employer.

I need to get all the employers but for some reason my query is returning total jobs which employer posted.

Please let me know what i'm going wrong in this query.

SELECT count(c.id) as total 
FROM employer as c INNER JOIN  job as j ON j.employerIDFK = c.id 
WHERE c.isActive=1 AND c.status=1 
AND j.isActive=1 
AND j.beenActive=1 
AND j.status=1 
AND DATE_ADD( j.createdAt, INTERVAL 30 DAY ) > NOW()
Was it helpful?

Solution

SELECT  COUNT(DISTINCT c.id) AS total 
FROM    employer c
JOIN    job j
ON      j.employerIDFK = c.id
WHERE   c.isActive = 1
        AND c.status = 1
        AND j.isActive = 1 
        AND j.beenActive = 1 
        AND j.status = 1 
        AND j.createdAt >= NOW() - INTERVAL 30 DAY

Create the following indexes:

employer (isActive, status)
job (employerFKID)
job (isActive, beenActive, status, createdAt, employerFKID)

for the query to work faster.

If for some obscure reasons you are reluctant to use DISTINCT, you may use this:

SELECT  COUNT(c.id) AS total
FROM    employer c
WHERE   c.isActive = 1
        AND c.status = 1
        AND c.id IN
        (
        SELECT  employerIDFK
        FROM    job j
        WHERE   j.isActive = 1
                AND j.beenActive = 1
                AND j.status = 1
                AND j.createdAt >= NOW() - INTERVAL 30 DAY
        )

, however, this may be less efficient, as MySQL cannot make job leading in this kind of query.

OTHER TIPS

Your query should be returning a count for all jobs for all employers, but there are some criteria that they must match to show up. An implicit join, as you are using, is an INNER JOIN. This requires all criteria to match in order for the rows to be included. This means it only returns jobs that are "isActive", "beenActive", "status=1", and createdAt is less than 30 days in the past, where employeres are "isActive" and "status=1". Check your data to see if this is what you want.

SELECT c.id AS employerID, count(*) as total 
FROM employer as c, job as j 
WHERE c.isActive=1 AND c.status=1 
AND j.employerIDFK = c.id 
AND j.isActive=1 
AND j.beenActive=1 
AND j.status=1 
AND DATE_ADD( j.createdAt, INTERVAL 30 DAY ) > NOW()
GROUP BY c.id

As far as what others are trying to say regarding the filter on the createdAt, MySQL will first evaluate NOW() (just once). It then adds 30 days to each createdAt date to see if it's greater than NOW(). MySQL will sometimes automatically optimize this, and it depends on your version and some other factors, but in general, performing a function against the createdAt date for each row to compare it to a constant expression is bad because MySQL can't utilize an index on the createdAt column.

So, you should convert:

AND DATE_ADD( j.createdAt, INTERVAL 30 DAY ) > NOW()

To this:

AND j.createdAt > DATE_ADD( NOW(), INTERVAL -30 DAY )

This leaves j.createdAt column as a plain column, so MySQL can now utilize any index against the column to find dates that are less than 30 days in the past.

It's the equivalent of standing in a room with 100 people and asking them to add 30 days to their birthday, then asking who's calculated date is greater than today. You just made 100 people do work. Instead, precalculate the criteria by subtracting 30 days from today's date and simply ask if anyone's birthday is greater than that date. You only had to do one calculation, and it saved those 100 people from having to do the hard work.

I suspect "SELECT count(*) as total" is your problem. You are asking SQL to count everything that is returned. Watch out for what you are "SELECT"-ing since that's the only thing which will be returned to you.

You didn't quite explain what you're trying to achieve here. "I need to get all the employers" is vague and hard to understand. What data are you trying to get about the employers? Their ID? Their total jobs?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top