Adding a string for the join condition on SQL - sql

I have two tables, articles and log. I'm trying to join the two tables a look only for the articles that appear in the log. The only relation that the two tables have is articles.slug which shows the title of the article and log.path which shows the same text of articles.slug but with '/article/' at the beginning. Exmaple:
This is the log.path: '/article/bad-things-gone'
This is the articles.slug: 'bad-things-gone'
I'm trying to do this:
SELECT articles.title, count
FROM articles join
(SELECT path, COUNT(*) as count
FROM log
GROUP BY path
ORDER BY count desc
) as a
ON ('/article/' + articles.slug) = a.path
but it is not working as it says I can not add the string '/articles/' to the articles.slug.
Is there a way to do this? Thanks.

I suspect you want:
SELECT a.title, l.count
FROM articles a join
(SELECT l.path, COUNT(*) as count
FROM log l
GROUP BY l.path
) l
ON ('/article/' || a.slug) = l.path
ORDER BY l.count desc;
Notes:
Postgres uses the standard operator || for string concatenation.
Ordering in a subquery has nothing to do with ordering in the outer query.
Use table aliases that relate to the table name or subquery.
Qualify all column names.

use the CONCAT function
SELECT CONCAT('/article/', column);

Related

Get Max from a joined table

I write this script in SQL server And I want get the food name with the Max of order count From this Joined Table . I can get Max value correct but when I add FoodName is select It give me an error.
SELECT S.FoodName, MAX(S.OrderCount) FROM
(SELECT FoodName,
SUM(Number) AS OrderCount
FROM tblFactor
INNER JOIN tblDetail
ON tblFactor.Factor_ID = tblDetail.Factor_ID
WHERE FactorDate = '2020-10-30'
GROUP BY FoodName)S
Here is The Error Message
Column 'S.FoodName' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
also I know I can use order by and top to achieve the food Name and Max order number but I want use the way I use in this script . Thank you for your answers
If I follow you correctly, you can use ORDER BY and TOP (1) directly on the result of the join query:
SELECT TOP (1) f.FoodName, SUM(d.Number) AS OrderCount
FROM tblFactor f
INNER JOIN tblDetail d ON f.Factor_ID = d.Factor_ID
WHERE f.FactorDate = '2020-10-30'
GROUP BY f.FoodName
ORDER BY OrderCount DESC
Notes:
I added table aliases to the query, and prefixed each column with the table it (presumably !) comes from; you might need to review that, as I had to make assumptions
If you want to allow top ties, use TOP (1) WITH TIES instead
You have an aggregation function in the outer query MAX() and an unaggregated column. Hence, the database expects a GROUP BY.
Instead, use ORDER BY and LIMIT:
SELECT FoodName, SUM(Number) AS OrderCount
FROM tblFactor f INNER JOIN
tblDetail d
ON fd.Factor_ID = d.Factor_ID
WHERE FactorDate = '2020-10-30'
GROUP BY FoodName
ORDER BY OrderCount DESC
LIMIT 1;
Note: In a query that references multiple tables, you should qualify all column references. It is not clear where the columns come from, so I cannot do that for this query.

SQL DISTINCT only for one column

I am trying to get the results from this query to only give me DISTINCT results by the concatenated column fullname. The results instead give me DISTINCT by both fullname and facilityname (which is 'name'). I've looked over other solutions to similar questions but don't understand them well enough to apply them to this situation.
I've tried to play with the code, but nothing worth noting.
USE country_club;
SELECT DISTINCT
CONCAT(mem.firstname, mem.surname) AS fullname,
fac.name
FROM
Bookings AS boo
JOIN
Members AS mem ON boo.memid = mem.memid
JOIN
Facilities AS fac ON boo.facid = fac.facid
WHERE
boo.facid = 0 OR boo.facid = 1
ORDER BY
fullname ASC;
No errors on this code to note. Just need to modify the conditions to arrive at the desired outcome.
If you want one row per group of columns, use group by, not distinct:
SELECT concat(m.firstname, m.surname) as fullname,
MIN(f.name)
FROM Bookings b JOIN
Members m
ON b.memid = m.memid JOIN
Facilities f
ON b.facid = f.facid
WHERE b.facid IN (0, 1)
GROUP BY concat(m.firstname, m.surname)
ORDER BY fullname ASC;
If you're using the mysql, before running your query, run this:
SET SESSION sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY', ''));

PostgreSQL - GROUP BY clause

I want to search by tags, and then list all articles with that tag, and also how many of given tags they match. So for example I might have:
Page1 - 2 (has css and php tag)
Page2 - 1 (has only css tag)
Query:
SELECT COUNT(t.tag)
FROM a_tags t
JOIN w_articles2tag a2t ON a2t.tag = t.id
JOIN w_article a ON a.id = a2t.article
WHERE t.tag = 'css' OR t.tag = 'php'
GROUP BY t.tag
LIMIT 9
When I only put COUNT(t.tag) the query works, and I get okay results. But if I append e.g. ID of my article I get following error:
ERROR: column "a.title" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT COUNT(t.tag), a.title FROM a_tags t
How to add said columns to this query?
Postgres 9.1 or later, quoting the release notes of 9.1 ...
Allow non-GROUP BY columns in the query target list when the primary
key is specified in the GROUP BY clause (Peter Eisentraut)
The SQL standard allows this behavior, and because of the primary key,
the result is unambiguous.
Related:
Return a grouped list with occurrences using Rails and PostgreSQL
The queries in the question and in #Michael's answer have the logic backwards. We want to count how many tags match per article, not how many articles have a certain tag. So we need to GROUP BY w_article.id, not by a_tags.id.
list all articles with that tag, and also how many of given tags they match
To fix this:
SELECT count(t.tag) AS ct, a.* -- any column from table a allowed ...
FROM a_tags t
JOIN w_articles2tag a2t ON a2t.tag = t.id
JOIN w_article a ON a.id = a2t.article
WHERE t.tag IN ('css', 'php')
GROUP BY a.id -- ... since PK is in GROUP BY
LIMIT 9;
Assuming id is the primary key of w_article.
However, this form will be faster while doing the same:
SELECT a.*, ct
FROM (
SELECT a2t.article AS id, count(*) AS ct
FROM a_tags t
JOIN w_articles2tag a2t ON a2t.tag = t.id
GROUP BY 1
LIMIT 9 -- LIMIT early - cheaper
) sub
JOIN w_article a USING (id); -- attached alias to article in the sub
Closely related answer from just yesterday:
Why does the following join increase the query time significantly?
When you use a "GROUP BY" clause, you need to enclose all columns that are not grouped in an aggregate function. Try adding title to the GROUP BY list, or selecting "min(a.title)" instead.
SELECT COUNT(t.tag), a.title FROM a_tags t
JOIN w_articles2tag a2t ON a2t.tag = t.id
JOIN w_article a ON a.id = a2t.article
WHERE t.tag = 'css' OR t.tag = 'php' GROUP BY t.tag, a.title LIMIT 9

SQL: Count in join not working

I have an SQL query where I simply join two tables. One table contain comments and the other is the user table. I join the tables to in a simple manner get user information about the user who wrote the comment (username) at the same time get the comment (comment text etc.).
Now I want to count the number of comments to write the correct number of comments on the top of the page. I do this by adding a COUNT, and an alias to save the value.
When I echo numCount, I get the correct value of comments, but I get no comments in my comment loop. As soon as I remove the count, I get all comments again. What am I doing wrong?
SELECT
ncID, ncText, ncDate,
uID, uName, uImageThumb,
COUNT(a.ncID) AS numComments
FROM tblNewsComments a LEFT JOIN tblUsers b
ON a.ncUserID = b.uID
WHERE a.ncNewsID = $newID
ORDER BY ncDate DESC
I am going to assume this is MySQL (or maybe SQLite), since most other RDBMS would fail on this query. The issue is that you are missing a GROUP BY clause, which is required when using an aggregate function like COUNT() unless it is to operate over the entire rowset. MySQL's unusual behavior is to allow the absence of a GROUP BY, or to allow columns in SELECT which are not also in the GROUP BY, producing unusual results.
The appropriate way to do this would be to join in a subquery which returns the COUNT() per ncID.
SELECT
ncID,
ncText,
ncDate,
uID,
uName,
uImageThumb,
/* The count returned by the subquery */
ccount.numComments
FROM
tblNewsComments a
LEFT JOIN tblUsers b ON a.ncUserID = b.uID
/* Derived table returns only ncID and count of comments */
LEFT JOIN (
SELECT ncID, COUNT(*) AS numComments
FROM tblNewsComments
GROUP BY ncID
) ccount ON a.ncID = ccount.ncID
WHERE a.ncNewsID = $newID
ORDER BY ncDate DESC
Edit Whoops - looks like you wanted the count per ncID, not the count per ncUserID as I originally had it.
I don't know what SQL engine you are using, but what you have here is not valid SQL and should be flagged as such.
COUNT is an aggregate function and you can only apply those to groups or a whole table, so in your case you would probably do
SELECT
ncID, ncText, ncDate,
uID, uName, uImageThumb,
COUNT(a.ncID) AS numComments
FROM tblNewsComments a LEFT JOIN tblUsers b
ON a.ncUserID = b.uID
WHERE a.ncNewsID = $newID
GROUP BY ncID, ncText, ncDate,
uID, uName, uImageThumb
ORDER BY ncDate DESC
You're using an AGGREGATE function (Count) but you're needing a GROUP BY to make any sense from that count.
I suggest adding "GROUP BY [all other field names except the COUNT]" to your query
Try this:
SELECT
ncID, ncText, ncDate,
uID, uName, uImageThumb,
(SELECT COUNT(ncID)
FROM
tblNewsComments a
INNER JOIN
tblUsers b
ON a.ncUserID = b.uID)
AS numComments
FROM tblNewsComments a LEFT JOIN tblUsers b
ON a.ncUserID = b.uID
WHERE a.ncNewsID = $newID
ORDER BY ncDate DESC

SQL WHEREing on a different table's COUNT

So, I want to apply a WHERE condition to a field assigned by a COUNT() AS clause. My query currently looks like this:
SELECT new_tags.tag_id
, new_tags.tag_name
, new_tags.tag_description
, COUNT(DISTINCT new_tags_entries.entry_id) AS entry_count
FROM (new_tags)
JOIN new_tags_entries ON new_tags_entries.tag_id = new_tags.tag_id
WHERE `new_tags`.`tag_name` LIKE '%w'
AND `entry_count` < '1'
GROUP BY new_tags.tag_id ORDER BY tag_name ASC
The bit that's failing is the entry_count in the WHERE clause - it doesn't know what the entry_count column is. My table looks like this:
new_tags {
tag_id INT
tag_name VARCHAR
}
new_tags_entries {
tag_id INT
entry_id INT
}
I want to filter the results by the number of distinct entry_ids in new_tags_entries that pertain to the tag ID.
Make sense?
Thanks in advance.
To filter on aggegated values use the HAVING clause...
SELECT
new_tags.tag_id, new_tags.tag_name,
new_tags.tag_description,
COUNT(DISTINCT new_tags_entries.entry_id) AS entry_count
FROM (new_tags)
JOIN new_tags_entries ON new_tags_entries.tag_id = new_tags.tag_id
WHERE `new_tags`.`tag_name` LIKE '%w'
GROUP BY new_tags.tag_id
HAVING COUNT(DISTINCT new_tags_entries.entry_id) < '1'
ORDER BY tag_name ASC
An inner join will never have a count of less than 1. Perhaps a left join and IS NULL would help. That, or using SUM() instead.
Although APC's answer will be syntactically correct, if the problem you are trying to solve is indeed: "Find me all new_tags that do not have any news_tags_entries", then the query with INNER JOIN and GROUP BY and HAVING will not yield the correct result. In fact, it will always yield the empty set.
As Ignacio Vazques Abrahams pointed out, a LEFT JOIN will work. And you don't even need the GROUP BY / HAVING:
SELECT news_tags.*
FROM news_tags
LEFT JOIN news_tags_entries
ON news_tags.tag_id = news_tags_entries.tag_id
WHERE news_tags_entries.tag_id IS NULL
(Of course, you can still add GROUP BY and HAVING if you are interested to know how many entries there are, and not just want to find news_tags with zero news_tags_entries. But the LEFT JOIN from news_tags to news_tags_entries needs to be there or else you'll lose the news_tags that have no corresponding items in news_tags_items)
Another, more explicit way to solve the "get me all x for which there is no y" is a correlated NOT EXISTS solution:
SELECT news_tags.*
FROM news_tags
WHERE NOT EXISTS (
SELECT NULL
FROM news_tags_entries
WHERE news_tags_entries.tag_id = news_tags.tag_id
)
Although nice and explicit, this solution is typically shunned in MySQL because of the rather bad subquery performance
SELECT
new_tags.tag_id, new_tags.tag_name,
new_tags.tag_description,
COUNT(DISTINCT new_tags_entries.entry_id) AS entry_count
FROM (new_tags)
LEFT JOIN new_tags_entries ON new_tags_entries.tag_id = new_tags.tag_id
WHERE `new_tags`.`tag_name` LIKE '%w'
GROUP BY new_tags.tag_id ORDER BY tag_name ASC
HAVING `entry_count` < '1'