MSSQL Group By failing, but no dup Column names - sql-server

I know this question has been asked time and time again, but I have no two column names that are the same, yet I am getting:
Msg 8120, Level 16, State 1, Line 13 Column 'dbo.PRODUCT.ProductName'
is invalid in the select list because it is not contained in either an
aggregate function or the GROUP BY clause.
My ProductId column is unique to my dbo.Product Table, and I am not sure why it is getting confused with another value. In this image you can see the dup ProductIds
WITH products AS
(
SELECT
*,
ROW_NUMBER() OVER(ORDER BY p.[ProductName]) AS 'RowNumber'
FROM dbo.PRODUCT p
JOIN dbo.Category c ON p.ProductCategoryCode = c.CategoryCode
JOIN dbo.Supplier s ON p.ProductSupplierCode = s.SupplierCode
LEFT JOIN dbo.ProductTag pt ON pt.ProductUPC = p.UPC
LEFT JOIN dbo.Tag t ON pt.ProductTagTagCode = t.TagCode
GROUP BY p.ProductId
)
SELECT *
FROM products
WHERE RowNumber BETWEEN 0 AND 2;

Your error is because you are selecting ALL of the fields in ALL of the tables, but you are only grouping by one value. If a value is returned by the query, then it must either be GROUPED or aggregated (Min, Max, SUM, AVG, etcetera).
If you simply add the Product Name to your grouping:
GROUP BY p.ProductId, p.ProductName
You will still have the same problem with (for example) p.ProductCategoryCode, p.ProductSupplierCode, c.CategoryCode, etc, etc.
In this case, where you are looking for unique rows, do not use GROUP BY - use DISTINCT (which works on all fields returned automatically) instead. Note that #bjones is still correct as to why you are getting duplicates - one of the tables you are joining in can have multiple rows for each product (e.g. many times a product will come from more than one supplier.)
To solve this, you need to:
Determine what data you need to return, and only select those columns
Determine if you need to summarize any data (i.e. Total Sold or On Hand), then:
Use GROUP BY if you do need to summarize any values, or
Use DISTINCT if you do not need to summarize any values

Related

How to get only last row after inner join?

I need to return unique funeral_homes who contains not completed leads and sort these by last lead timestamp.
This is my sql query:
select distinct h.funeral_home_name, h.funeral_home_guid, h.address1, h.city, h.state, p.discount_available, t.date_created
from tblFuneralHomes h inner join tblFuneralTransactions t on h.funeral_home_guid = t.funeral_home_guid
inner join vwFuneralHomePricing p on h.funeral_home_guid = p.funeral_home_guid where completed=0 order by 'funeral_home_name' asc;
This is the result, but I need only unique homes with last added lead
What I should change here?
The problem here appears that you are joining into tables with 1 to many relationships with table tblFuneralHomes, yet you expect only one row per funeral home.
Instead of using distinct, I would suggest that instead you group by the required output funeral home columns, and then apply some kind of aggregate on the columns needed from the joined tables in order to return just a single computed value from all possible joined values.
For instance, below we find the first transaction date (min) associated with each funeral home:
select h.funeral_home_name, h.funeral_home_guid, h.address1, h.city, h.state,
p.discount_available, min(t.date_created)
from tblFuneralHomes h
inner join tblFuneralTransactions t on h.funeral_home_guid = t.funeral_home_guid
inner join vwFuneralHomePricing p on h.funeral_home_guid = p.funeral_home_guid
where completed=0
group by h.funeral_home_name, h.funeral_home_guid, h.address1, h.city, h.state,
p.discount_available
order by h.funeral_home_name asc
Note that depending on the cardinality of the association between tblFuneralHomes and vwFuneralHomePricing, you may also need to remove p.discount_available from the grouping and also introduce it with an aggregate function, similar to what I've done with t.date_created

Using SQL to combine detailed and aggregated results

I am developing a report against a SQL Server database. Using the query presented here...
SELECT
f.FacilityID as 'FID',
COUNT (DISTINCT f.PhoneTypeID) as 'Ptypes',
COUNT (DISTINCT f.PhoneID) as 'Pnumbers'
from dbo.FacilityPhones as f
inner join
dbo.Phones as ph
f.PhoneID = ph.PhoneID
group by f.FacilityID
having COUNT(DISTINCT f.PhoneTypeID)<>COUNT(DISTINCT f.PhoneId);
...I have identified 107 records where the number of phone numbers present for a Facility differs from the number of phone number types (e.g., there are two distinct phone numbers, both listed as primary).
I would like to be able to produce a detailed report that would list phone numbers and phone types for each facility, but ONLY when the distinct counts differ.
Is there a way to do this with a single query? Or would I need to save the summaries to a temp table, then join back to that temp table to get the details?
Not sure what fields exist in dbo.Phone; but assume the number comes from there... Likely need to join to the type table to get it's description as well...
This uses a common table expression to get your base list of items an then a correlated subquery to ensure only those facilities in your cte are displayed.
WITH CTE AS (
SELECT f.FacilityID as 'FID'
, COUNT (DISTINCT f.PhoneTypeID) as 'Ptypes'
, COUNT (DISTINCT f.PhoneID) as 'Pnumbers'
FROM dbo.FacilityPhones as f
GROUP BY f.FacilityID
HAVING COUNT(DISTINCT f.PhoneTypeID)<>COUNT(DISTINCT f.PhoneId))
SELECT *
FROM dbo.FaclityPhones FP
INNER JOIN dbo.Phones as ph
ON FP.PhoneID = ph.PhoneID
WHERE EXISTS (SELECT 1
FROM CTE
WHERE FID = FP.FacilityID)
The where clause here just says only show those FacilityID's and associated records if the FacilityID exists in your original query (CTE) (107) If we needed data from the CTE we'd join to it; but as it's simply restricting data placing it in the where clause and using an exists will likely be more efficient.

SQL query where person has as request for every product

I have a shortcut query where I just counted the total number of products and used count to display that the person has requested that many products. (Which is 8 products)
I want to know if there's an easier way where I wouldn't need to count the the products myself and have the query do it. Basically, replace the 8 with the total amount of products that the database has.
SELECT DISTINCT
Tb_Consumer.Name
FROM
Tb_Consumer, Tb_Product, Tb_Requests
WHERE
Tb_Consumer.Con_ID = Tb_Requests.Con_ID
AND Tb_Requests.Prod_ID = Tb_Product.Prod_ID
GROUP BY
Tb_Consumer.Name
HAVING
COUNT(Tb_Product.Name) = 8
Use a subquery to find the number of products in the Tb_Product table:
SELECT
tbc.Name
FROM Tb_Consumer tbc
INNER JOIN Tb_Request tbr
ON tbc.Con_ID = tbr.Con_ID
INNER JOIN Tb_Product tbp
ON tbr.Prod_ID = tbp.Prod_ID
GROUP BY
tbc.Name
HAVING
COUNT(tbp.Name) = (SELECT COUNT(*) FROM Tb_Product); -- count products here
This assumes that every record in Tb_Product corresponds to a single unique product. If there could be duplication for some reason, then you can count distinct products, e.g.
(SELECT COUNT(DISTINCT Name) FROM Tb_Product)
Other changes I made include removing DISTINCT from the select clause, since the GROUP BY should already make each name distinct. I also refactored your query to remove the commas in the FROM clause. Instead, I use explicit joins between the three tables.

Right way to use distinct in SQL Server

I am trying to retrieve some records based on the query
Select distinct
tblAssessmentEcosystemCredit.AssessmentEcosystemCreditID,
tblSpecies.CommonName
from
tblAssessmentEcosystemCredit
left join
tblSpeciesVegTypeLink on tblAssessmentEcosystemCredit.VegTypeID = tblSpeciesVegTypeLink.VegTypeID
left join
tblSpecies on tblSpecies.SpeciesID = tblSpeciesVegTypeLink.SpeciesID
where
tblAssessmentEcosystemCredit.SpeciesTGValue < 1
The above query returns 17,000 records but when I remove tblSpecies.CommonName, it retrieves only 4200 (that's actually correct).
I have no idea how to distinct only tblAssessmentEcosystemCredit.AssessmentEcosystemCreditID column and retrieve all other table columns in the query.
This query selects the different COMBINATION of AssessmentEcosystemCreditID and CommonName; if you want only one row per value of AssessmentEcosystemCreditID then you need to use a GROUP BY, as suggested by #JonasB; however, in that case, there could be several values of CommonName per value of AssessmentEcosystemCreditID , and so SQL requires you to specify WHICH one you want
Select tblAssessmentEcosystemCredit.AssessmentEcosystemCreditID ,
max(tblSpecies.CommonName) as CommonName,
min(tblSpecies.CommonName) as CommonName2, -- so you can verify you only have one value
from tblAssessmentEcosystemCredit
left join tblSpeciesVegTypeLink
on tblAssessmentEcosystemCredit.VegTypeID = tblSpeciesVegTypeLink.VegTypeID
left join tblSpecies on tblSpecies.SpeciesID= tblSpeciesVegTypeLink.SpeciesID
where tblAssessmentEcosystemCredit.SpeciesTGValue <1
GROUP BY tblAssessmentEcosystemCredit.AssessmentEcosystemCreditID
See this topic: mySQL select one column DISTINCT, with corresponding other columns
You probably have to deactivate ONLY_FULL_GROUP_BY, see http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_only_full_group_by

How do I get the count of rows from two different tables in one result?

I have two tables from which I need to extract the NUMBER of rows belonging to the same ID.
Orders Table
Positions Table
I'm trying to build a query that will get me the amount of matching rows from the two tables, based on a common ID. I'm not looking for a result set that will return all matching rows, I only need back one value with the amount of matching rows found.
I've tried this next query, but I know there are in fact 48 rows when it returns 50.
select Isnull(CntPos,0)+IsNull(CntOrd,0)
from (select CID
,Count(*) CntPos
from [Serv].[Trade].[Position]
Group By CID
) Pos
Full join (select CID
,Count(*) CntOrd
from [Serv].[Stocks].[Orders]
Group By CID
) Ord
on Ord.[CID] = Pos.CID
WHERE Ord.CID=19556

Resources