1

This CF query does what I want but I'd like to show only one occurrence of each matching "HEADLINE" that has a count greater than 5. I don't need to display the actual count, just the headline which will be a link using that headline as a url variable. And there must be a more efficient way for my code to work?

<cfquery name="HeadInfo" datasource="certify">
    select headline
    from post
    where deleted = 0
    and headline IS NOT NULL
    order by altid desc
</cfquery>

<cfoutput>#HeadInfo.RecordCount#</cfoutput>

<cfoutput query="HeadInfo">

    <cfquery name="CountInfo" datasource="certify">
        select *
        from post
        where deleted = 0
        and headline = '#HeadInfo.Headline#'
        order by headline desc
    </cfquery>

<cfif CountInfo.RecordCount GT 5>
  #HeadInfo.headline# - Count:#CountInfo.RecordCount#<br>
</cfif>
</cfoutput>

rrk
  • 15,677
  • 4
  • 29
  • 45
  • 1
    You can try to use the [`group by`](https://www.w3schools.com/sql/sql_groupby.asp) in SQL and get the count. – rrk Jan 24 '22 at 17:22
  • 1
    You should join those two tables, use `count(*)` and `group by` to get the data you need in a single SQL statement. If you proceed as you are, you're generating a query "CountInfo" for every record in "HeadInfo". So if you get 10 records in the first, you end up with 11 queries total. You can do all of that in one, including a filter where the count > 5. Check the `LEFT JOIN` example here: https://davidhamann.de/2017/07/11/sql-get-the-count-of-related-records/ – Adrian J. Moreno Jan 24 '22 at 17:35
  • 1
    Also if you're *only* using the headline text, and not other fields, you don't even need a JOIN, just the count(*) and group by. Then use `having count(*) > 5` to get records with a count > 5 – SOS Jan 24 '22 at 21:36
  • SQL syntax is often vendor specific. Please update the question tags to indicate your DBMS and version (sql server 2016, mysql 8, ...) – SOS Feb 03 '22 at 09:50

2 Answers2

4

Let's assume you have a MySQL or MariaDB table named post with the following structure:

CREATE TABLE IF NOT EXISTS `post` (
  `altid` int(11) NOT NULL AUTO_INCREMENT,
  `headline` varchar(50) DEFAULT NULL,
  `post` varchar(255) DEFAULT NULL,
  `deleted` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`altid`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4;

Populated with some test values like these:

INSERT INTO `post` (`altid`, `headline`, `post`, `deleted`) VALUES
    (1, 'headline1', 'post headline1 is foo', 0),
    (2, 'headline1', 'post headline1 is bar', 0),
    (3, 'headline1', 'post headline1 is foobar', 0),
    (4, 'headline1', 'post headline1 is contoso', 0),
    (5, 'headline1', 'post headline1 is contoso foo', 0),
    (6, 'headline1', 'post headline1 is contoso bar', 0),
    (7, 'headline2', 'post headline2 is foo is deleted', 1),
    (8, 'headline2', 'post headline2 is bar', 0),
    (9, 'headline2', 'post headline2 is barFoo', 0),
    (10, 'headline2', 'post headline2 is barFoo contoso', 0),
    (11, 'headline2', 'post headline2 is foo contoso', 0),
    (12, 'headline2', 'post headline2 is contoso bar', 0),
    (13, NULL, 'post headline3 is bar with NULL headline', 0),
    (14, 'headline2', 'post headline2 of another post', 0),
    (15, 'headline2', 'post headline2 of another foobar post', 0),
    (16, 'headline3', 'post headline3 some post for count<5', 0);

You could achieve this even without a JOIN as some have suggested, because it's the same table and this kind of SELECT won't need to have the table JOINED to itself.

In case you need to output the headline recordcount of all headlines (including those < 5), but output only the hits with counts > 5, do it with just one SELECT like follows:

<cfquery name="HeadInfo" datasource="certify">
    SELECT      headline, count( headline ) as headlineCount -- aggregate function count()
    FROM        post
    WHERE       deleted = 0
                and headline IS NOT NULL
    GROUP BY    headline    -- group by for aggregate count() function
    ORDER BY    altid desc
</cfquery>


<cfoutput>
  Total of headlines #HeadInfo.recordcount#<br>
  <cfloop query="HeadInfo">
    <cfif HeadInfo.headlineCount GT 5>
        #HeadInfo.headline# - Count:#HeadInfo.headlineCount#<br>
    </cfif>
  </cfloop>
</cfoutput>

The output is:

Total of headlines 3
headline2 - Count:7
headline1 - Count:6

But if the recordcount of all headlines isn't needed, then use the aggregate SQL function count() with GROUP BY using the HAVING clause to select only the headlines with count > 5 already in SQL. That will reduce payload inbetween the DB connection with the CF Engine (please see my SQL-Comments inbetween the lines):

<cfquery name="HeadInfo" datasource="certify">
    SELECT      headline, count( headline ) as headlineCount -- aggregate function count()
    FROM        post
    WHERE       deleted = 0
                and headline IS NOT NULL
    GROUP BY    headline    -- group by for aggregate count() function
    HAVING      count( headline ) > 5 -- get headlines with more than 5 posts only (aggregate function count)
    ORDER BY    altid desc
</cfquery>


<cfoutput>
  <cfloop query="HeadInfo">
    #HeadInfo.headline# - Count:#HeadInfo.headlineCount#<br>
  </cfloop>
</cfoutput>

The output is:

headline2 - Count:7
headline1 - Count:6
AndreasRu
  • 1,053
  • 8
  • 14
  • 1
    I wish I could upvote this more for the extra effort put into setting up the table with data and the thorough explanation. – Adrian J. Moreno Jan 28 '22 at 15:02
  • Thanks @AdrianJ.Moreno. Feel very honoured you've mentioned that. I like posts that allow to test code quickly, that is why I like to share it all like that. – AndreasRu Feb 02 '22 at 22:16
2

You need to do it in one query.

<cfquery name="HeadInfo" datasource="headlines">
SELECT          P.headline
FROM            post as P
    INNER JOIN  (
                    SELECT      headline
                    FROM        post
                    WHERE       deleted = 0
                    GROUP BY    headline
                        HAVING  COUNT(headline) > 5
                ) AS PC
        ON      P.headline = PC.headline
WHERE           P.deleted = 0
    AND         P.headline IS NOT NULL
ORDER BY        altid DESC
</cfquery>

<ul>
    <cfoutput query="HeadInfo">
        <li>#HeadInfo.headline#</li>
    </cfoutput>
</ul>
baynezy
  • 6,493
  • 10
  • 48
  • 73
  • 1
    Did you test your code? I think you have some problems in it: 1. you need to close the doublequote at – AndreasRu Jan 25 '22 at 16:33