Hi all I have a problem with an SQL query: the problem is that if i add GROUP BY the database engine outputs the error:
Column 'dbo.classes.class_name' is invalid in the select list because
it is not contained in either an aggregate function or the GROUP BY clause.
My query is:
string query = "SELECT p.*
FROM dbo.classes AS p INNER JOIN teacher_classes AS a
ON a.class_id = p.class_id
and teach_id = #id
GROUP BY p.class_id";
Is there any help please for that.
Note without group by the query work fine but the result not grouped.
Your query is:
SELECT p.*
FROM dbo.classes AS p INNER JOIN
teacher_classes AS a
ON a.class_id = p.class_id and teach_id = #id
GROUP BY p.class_name;
You are trying to select all the columns from p and yet you're are grouping by class_name. This is not allowed in most databases. What happens if you have two classes, but information is different from them?
One option is to use distinct rather than group by to remove duplicates:
SELECT distinct c.*
FROM dbo.classes c INNER JOIN
teacher_classes tc
ON tc.class_id = c.class_id and tc.teach_id = #id;
Another option is to use something like in to find the matching classes for the teacher:
select c.*
from classes c
where c.class_id in (select tc.class_id from teacher_classes where teach_id = #id)
Notice I also changed your aliases so they have some relationship to the table names. This makes the query much easier to read.
Related
I have two tables. First one is name of all members and second is all projects and its team members of different roles.
Table 1 : [members] id, name
Table 2 : [projects] id, proj_name, sponsor (fk1_tbl_1), proj_mgr(fk2_tbl_1) , proj_co (fk3_tbl_1)
I created a query to show the project name and names of all project roles.
I am doing three joins with two sub-queries in order to achieve this.
I want to know if there is better ways to do this (in pure sql, NOT script languages like pl/sql).
select f.proj_name, f.proj_sponsor, f.proj_mgr, e.name proj_co
from
name e,
(
select
d.proj_name, d.proj_sponsor, c.name proj_mgr, d.proj_co
from
members c,
(
select
b.proj_name, a.name proj_sponsor, b.proj_mgr mgr, b.proj_co co
from
members a, projects b
where
b.sponsor = a.id
) d
where
c.id = d.mgr
) f
where
e.id = f.proj_co
Use join and join again:
select p.*, ms.name as sponsor, mm.name as manager, mc.name as co_name
from projects p left join
members ms
on p.sponsor = ms.id left join
members mm
on p.manager = mm.id left join
members mo
on p.proj_co = mo.id;
Notes:
This uses left join in case any values are missing. The project will still be returned.
Never use commas in the FROM clause.
Always use proper, explicit, standard JOIN syntax.
Use meaningful table aliases, rather than arbitrary letters.
Having trouble with filtering in my sqlite3 query. I am working with three tables below.
Table: models
id|data
1|car
2|truck
Table: descriptions
id|model_id|colour|make
1|1|blue|accord
2|1|green|prius
3|1|red|fusion
4|1|black|civic
5|1|white|jeep
6|1|purple|jeep
7|1|brown|jeep
8|1|brown|civic
Table: banned
model_id|colour_id|colour
1|3|black|
1|15|brown|
The below statement is counting how many of what model (cars or trucks) are what make.
SELECT models.id, make, count(make)
FROM description
JOIN models ON models.id = descriptions.model_id
GROUP BY models.id, descriptions.make;
The output would below
1|accord|1
1|prius|1
1|fusion|1
1|civic|2
1|jeep|3
However, I want to put in a qualifier that voids anything containing a banned colour/model combo, by using banned.colour.
I tried joining the table and filtering out like below, but it seems to double the count.
SELECT models.id, make, count(make)
FROM description
JOIN models ON models.id = descriptions.model_id
JOIN banned ON banned.model_id = models.id
WHERE NOT ( banned.colour = descriptions.colour)
GROUP BY models.id, descriptions.make;
My desired output is to void the two cars that fit this qualifier from the count. The final result should below
1|accord|1
1|prius|1
1|fusion|1
1|jeep|2
How can i achieve this?
You can use your approach . . . with a left join and where check:
SELECT m.id, d.make, count(*)
FROM description d JOIN
models m
ON m.id = d.model_id LEFT JOIN
banned b
ON b.model_id = m.id AND b.colour = d.colour
WHERE b.model_id IS NULL -- no match
GROUP BY m.id, d.make;
A common way to write the query would also use NOT EXISTS:
SELECT m.id, d.make, count(*)
FROM description d JOIN
models m
ON m.id = d.model_id
WHERE NOT EXISTS (SELECT 1
FROM banned b
WHERE b.model_id = m.id AND b.colour = d.colour
)
GROUP BY m.id, d.make;
Although you can also use NOT IN, I highly discourage using it with a subquery. It will not do what you want if any of the values returned by the subquery are NULL.
You may use NOT IN with a dependent subquery to avoid banned combinations.
SELECT models.id, make, count(make)
FROM description
JOIN models ON models.id = descriptions.model_id
WHERE colour NOT IN (
SELECT colour
FROM banned
WHERE banned.model_id = models.id
)
GROUP BY models.id, descriptions.make;
The only pitfall of NOT IN is a possibility to have a NULL in the subquery result. However, I believe that it is safe if the colour attribute is defined as NOT NULL (which make sense in this case).
Having trouble with my sql query. Not an SQL expert by any means.
SELECT
transactions.*,
categories.*,
GROUP_CONCAT(tags.tagName) as concatTags
FROM transactions
INNER JOIN categories
ON transactions.category = categories.categoryId
LEFT JOIN TransactionTagRelation AS ttr
ON transactions.transactionId = ttr.transactionId
LEFT JOIN tags
ON tags.tagId = ttr.tagId;
(There's also a where and group by, but didn't think it was relevant to the question).
I'm trying to get:
transactionId1, ...otherStuff..., "tagId1,tagId2,tagId3"
transactionId2, ...otherStuff..., "tagId1,tagId3"
What I have now seems to merge the tags into one transaction or something. I tried adding a GROUP BY transactionID at the end, but it gives a syntax error for some reason. I have a feeling my joins are incorrect, but I wasn't able to get anything better.
Do something like this:
SELECT t.*, c.*,
(SELECT GROUP_CONCAT(tg.tagName)
FROM TransactionTagRelation ttr JOIN
Tags tg
ON tg.tagId = ttr.tagId
WHERE t.transactionId = ttr.transactionId
) as concatTags
FROM transactions t JOIN
categories c
ON t.category = c.categoryId;
This eliminates the GROUP BY in the outer query and allows you to use t.* and c.* in the SELECT.
I already read a lot on that topic but I´m unable to get it to work for my case.
I have the following situation:
A list of orderitems (the main datasets I want to get)
Articles which have a 1:1 relation to an order item
A n:m Jointable "Articlesupplier" which creates a relation between an article and a
partner
A Partner table with detailed information about partners.
Target:
One dataset per OrderItem and from the suppliers I only want to get the first one found in the join. No priorization required.
Tables:
Table IDX_ORDERITEM
id,article_id
Table IDX_ARTICLE
id,name
Table IDX_ARTICLESUPPLIER
article_id,partner_id
Table IDX_PARTNER
id,abbr
My actual statement (short version):
SELECT IDX_ORDERITEM.id
FROM
dbo.IDX_ORDERITEM AS IDX_ORDERITEM
-- ARTICLE --
INNER JOIN dbo.IDX_ARTICLE AS IDX_ARTICLE
ON IDX_ORDERITEM.article_id=IDX_ARTICLE.id
-- SUPPLIER VIA ARTICLE --
LEFT JOIN
(SELECT TOP(1) IDX_PARTNER.id, IDX_PARTNER.abbr
FROM IDX_PARTNER, IDX_ARTICLESUPPLIER
WHERE IDX_PARTNER.id = IDX_ARTICLESUPPLIER.partner_id
AND IDX_ARTICLESUPPLIER.article_id=IDX_ARTICLE.id) AS IDX_PARTNER_SUPPLIER
ON IDX_PARTNER_SUPPLIER.id=IDX_ARTICLE.supplier_partner_id
WHERE 1>0
ORDER BY orderitem.id DESC
But it seems I can´t access IDX_ARTICLE.id in the subquery. I get the following error message:
The multi-part identifier "IDX_ARTICLE.id" could not be bound.
Is the problem that the Article alias has the same name as the table name?
Thanks a lot in advance for possible ideas,
Mike
Well, I changed your aliases, and the subquery to which you were joining (I also modified that subquery so it doesn't use implicit joins anymore), though this changes where mostly cosmetics. The actual important change was the use of OUTER APPLY instead of LEFT JOIN:
SELECT OI.id
FROM dbo.IDX_ORDERITEM AS OI
INNER JOIN dbo.IDX_ARTICLE AS A
ON OI.article_id = A.id
OUTER APPLY
(SELECT TOP(1) P.id, P.abbr
FROM IDX_PARTNER AS P
INNER JOIN IDX_ARTICLESUPPLIER AS SUP
ON P.id = SUP.partner_id
WHERE SUP.article_id = A.id
AND P.id = A.supplier_partner_id) AS PS
ORDER BY OI.id DESC
The error is thrown because the below piece of query
(SELECT TOP(1) IDX_PARTNER.id, IDX_PARTNER.abbr
FROM IDX_PARTNER, IDX_ARTICLESUPPLIER
WHERE IDX_PARTNER.id = IDX_ARTICLESUPPLIER.partner_id
AND IDX_ARTICLESUPPLIER.article_id=IDX_ARTICLE.id) AS IDX_PARTNER_SUPPLIER
cannot be considered as a correlated sub-query and IDX_ARTICLE.id is referenced in it in the same manner we reference a field of outer query in a correlated sub-query.
I see two problems.
According to your DDLs there is no IDX_ARTICLE.supplier_partner_id which you refer to in the left join on clause.
Second, I'm quite sure you cannot use IDX_ARTICLE.id in your derived table. Simply add IDX_ARTICLESUPPLIER.article_id to your derived table selected fields and use it in your left join on clause against IDX_ARTICLE.id.
I prefer to avoid nested queries. If I can, I will always rewrite it using CTE.
WITH Part_Sup
AS (
SELECT TOP ( 1 ) P.id
,P.abbr
,SUP.article_id
FROM IDX_PARTNER AS P
INNER JOIN IDX_ARTICLESUPPLIER AS SUP
ON P.id = SUP.partner_id
)
SELECT OI.id
FROM dbo.IDX_ORDERITEM AS OI
INNER JOIN dbo.IDX_ARTICLE AS A
ON OI.article_id = A.id
LEFT OUTER JOIN Part_Sup AS PS
ON PS.article_id = A.Id
AND PS.id = A.supplier_partner_id
ORDER BY OI.id DESC;
Next I rewritten the first query to use ROW_NUMBER() function instead of using TOP (1) using ROW_NUMBER you can control which results you want and what you don't want.
WITH Part_Sup
AS (
SELECT P.id
,P.abbr
,SUP.article_id
,ROW_NUMBER() OVER ( PARTITION BY P.id, P.abbr ) AS RowNum
FROM IDX_PARTNER AS P
INNER JOIN IDX_ARTICLESUPPLIER AS SUP
ON P.id = SUP.partner_id
)
SELECT OI.id
FROM dbo.IDX_ORDERITEM AS OI
INNER JOIN dbo.IDX_ARTICLE AS A
ON OI.article_id = A.id
LEFT OUTER JOIN Part_Sup AS PS
ON PS.article_id = A.Id
AND PS.id = A.supplier_partner_id
AND RowNum = 1
ORDER BY OI.id DESC;
Thanks Lamak - you solved it :)
I used your input to extract the basic solution to make it a bit easier to read for others which have the same problem:
Using OUTER APPLY (without ORDER_ITEM Table here):
SELECT IDX_ARTICLE.id AS AR_ID, IDX_PARTNER_SUPPLIER.id, IDX_PARTNER_SUPPLIER.abbr
FROM
dbo.IDX_ARTICLE AS IDX_ARTICLE
OUTER APPLY
(SELECT TOP(1) _PARTNER.id, _PARTNER.abbr
FROM IDX_PARTNER AS _PARTNER
INNER JOIN IDX_ARTICLESUPPLIER AS _ARTICLESUPPLIER
ON _PARTNER.id = _ARTICLESUPPLIER.partner_id
WHERE _ARTICLESUPPLIER.article_id=IDX_ARTICLE.id
AND _ARTICLESUPPLIER.deleted IS NULL) AS IDX_PARTNER_SUPPLIER
WHERE IDX_ARTICLE.id=67
please can you help me before I go out of my mind. I've spent a while on this now and resorted to asking you helpful wonderful people. I have a search query:
SELECT Groups.GroupID,
Groups.GroupName,
( SELECT Sum(SiteRates.SiteMonthlySalesValue)
FROM SiteRates
WHERE InvoiceSites.SiteID = SiteRates.SiteID
) AS SumOfSiteRates,
( SELECT Count(InvoiceSites.SiteID)
FROM InvoiceSites
WHERE SiteRates.SiteID = InvoiceSites.SiteID
) AS CountOfSites
FROM (InvoiceSites
INNER JOIN (Groups
INNER JOIN SitesAndGroups
ON Groups.GroupID = SitesAndGroups.GroupID
) ON InvoiceSites.SiteID = SitesAndGroups.SiteID)
INNER JOIN SiteRates
ON InvoiceSites.SiteID = SiteRates.SiteID
GROUP BY Groups.GroupID
With the following table relationship
http://m-ls.co.uk/ExtFiles/SQL-Relationship.jpg
Without the GROUP BY entry I can get a list of the entries I want but it drills the results down by SiteID where instead I want to GROUP BY the GroupID. I know this is possible but lack the expertise to complete this.
Any help would be massively appreciated.
I think all you need to do is add groups.Name to the GROUP BY clause, however I would adopt for a slightly different approach and try to avoid the subqueries if possible. Since you have already joined to all the required tables you can just use normal aggregate functions:
SELECT Groups.GroupID,
Groups.GroupName,
SUM(SiteRates.SiteMonthlySalesValue) AS SumOfSiteRates,
COUNT(InvoiceSites.SiteID) AS CountOfSites
FROM (InvoiceSites
INNER JOIN (Groups
INNER JOIN SitesAndGroups
ON Groups.GroupID = SitesAndGroups.GroupID
) ON InvoiceSites.SiteID = SitesAndGroups.SiteID)
INNER JOIN SiteRates
ON InvoiceSites.SiteID = SiteRates.SiteID
GROUP BY Groups.GroupID, Groups.GroupName;
I think what you are looking for is something like the following:
SELECT Groups.GroupID, Groups.GroupName, SumResults.SiteID, SumResults.SumOfSiteRates, SumResults.CountOfSites
FROM Groups INNER JOIN
(
SELECT SitesAndGroups.SiteID, Sum(SiteRates.SiteMonthlySalesValue) AS SumOfSiteRates, Count(InvoiceSites.SiteID) AS CountOfSites
FROM SitesAndGroups INNER JOIN (InvoiceSites INNER JOIN SiteRates ON InvoiceSites.SiteID = SiteRates.SiteID) ON SitesAndGroups.SiteID = InvoiceSites.SiteID
GROUP BY SitesAndGroups.SiteID
) AS SumResults ON Groups.SiteID = SumResults.SiteID
This query will group your information based on the SiteID like you want. That query is referenced in the from statement linking to the Groups table to pull the group information that you want.