How to use Group By in certain Select in SQL Server? - sql

I haven't used Group By that much before and always get an error when trying to group the following by the UserID
Error:
Column 'Log.Version' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
How can I avoid this ? What I am trying to get is a count for each user with every user only appearing once for each tool instead of listing every usage separately.
My query (working except for the above + simplified for demo):
ALTER PROCEDURE [dbo].Reporting_FetchUsage
AS
BEGIN
SET NOCOUNT ON;
SELECT
A.UserID, A.Tool, A.[Version], A.Note, A.[TimeStamp],
B.ADesc, B.SDesc, B.DDesc, B.ALN
FROM
Log A
LEFT JOIN
EmployeeTable B ON B.NTID = A.UserID
WHERE
A.[TimeStamp] > DATEADD(YEAR, -1, GETDATE())
AND (A.Tool LIKE 'abc%'
OR
A.Tool LIKE 'def%'
OR
A.Tool LIKE 'ghi%')
AND (B.DDesc = 'CB'
OR
B.DDesc = 'PS')
GROUP BY
A.Tool, A.UserID
ORDER BY
A.Tool, A.UserID
FOR XML PATH('reporting'), ELEMENTS, TYPE, ROOT('ranks')
END

What I am trying to get is a count for each user with every user only
appearing once for each tool instead of listing every usage
separately.
If this is the case, I would expect to see a count() somewhere. Perhaps this is closer to what you want:
SELECT l.UserID, l.Tool, e.ADesc, e.SDesc, e.DDesc, e.ALN,
COUNT(*) as NumTools
FROM Log l LEFT JOIN
EmployeeTable e
ON e.NTID = l.UserID
WHERE l.[TimeStamp] > DATEADD(YEAR, -1, GETDATE()) AND
(l.Tool LIKE 'abc%' OR l.Tool LIKE 'def%' or l.Tool LIKE 'ghi%') AND
B.DDesc IN ( 'CB', 'PS')
GROUP BY l.UserID, l.Tool, e.ADesc, e.SDesc, e.DDesc, e.ALN,
ORDER BY l.Tool, l.UserID;
This assumes that the employee table does not have duplicates, with respect to the join conditions on the Log.

Include all the fields in your GROUP BY clause that are in your SELECT statement that are not being used in an aggregate.
ALTER PROCEDURE [dbo].Reporting_FetchUsage
AS
BEGIN
SET NOCOUNT ON;
SELECT A.UserID,
A.Tool,
A.[Version],
A.Note,
A.[TimeStamp],
B.ADesc,
B.SDesc,
B.DDesc,
B.ALN
FROM Log A
LEFT JOIN EmployeeTable B
ON B.NTID = A.UserID
WHERE A.[TimeStamp] > DATEADD(YEAR, -1, GETDATE())
AND (
A.Tool LIKE 'abc%'
OR
A.Tool LIKE 'def%'
OR
A.Tool LIKE 'ghi%'
)
AND (
B.DDesc = 'CB'
OR
B.DDesc = 'PS'
)
GROUP BY A.Tool,
A.UserID,
A.[Version],
A.Note,
A.[TimeStamp],
B.ADesc,
B.SDesc,
B.DDesc,
B.ALN
ORDER BY A.Tool, A.UserID
FOR XML PATH('reporting'), ELEMENTS, TYPE, ROOT('ranks')
END

Related

I need to add additional columns to my application that are not part of GROUPBY statement

I need some help with my SQL command. I have a VB.net application that uses few sql tables to get the final result. I need to add few additional columns from TableC into my application, but these columns are not part of GROUPBY function.
I tried to add simple select function to the code, but I always get the same error:
$"
SET NOCOUNT ON;
SELECT CONVERT(VARCHAR(10), r.PossibleDate, 120) as [Date],
**SELECT r.GStart as GStart,**
SUM(ISNULL(r.ElecCheckIn, 0)) as CheckIn,
SUM(ISNULL(r.ElecCheckOut, 0)) as CheckOut,
SUM(ISNULL(r.JPAmount, 0)) as AttAmountJP,
SUM(ISNULL(r.MeteredAttAmountCC, 0)) as AttAmountCC,
SUM(ISNULL(r.MeteredMachAmount, 0)) as MachAmount,
SUM(ISNULL(r.MeteredAttAmount, 0)) as AttAmount,
SUM(ISNULL(r.ElecCheckIn, 0) - ISNULL(r.ElecCheckOut, 0) - ISNULL(r.JPAmount, 0) - ISNULL(r.MeteredAttAmountCC, 0) - ISNULL(r.MeteredMachAmount, 0) - ISNULL(r.MeteredAttAmount, 0)) as NetWin
FROM dbo.CDS_TableA sm (NOLOCK)
INNER JOIN dbo.bb_tableB st (NOLOCK)
ON sm.TableB_Id=st.SlotB_Id
AND sm.TableBRevision=st.TabelBRevision
INNER JOIN dbo.TableC r (NOLOCK)
ON sm.TableA_ID=r.TableA_ID
AND r.PossibleDate BETWEEN '{dtStart.Value.ToString("yyyy-MM-dd")} 00:00:00' AND '{dtEnd.Value.ToString("yyyy-MM-dd")} 23:59:59'
AND r.Period_ID=4
INNER JOIN dbo.BB_TableD rh (NOLOCK)
ON sm.TableA_ID=rh.TableA_ID
AND r.PossibleDate=rh.PossibleDate
AND sm.Revision=rh.Revision
WHERE sm.OnFloorFlag = 1
AND sm.Calc_ID NOT IN (2,5)
GROUP BY r.PossibleDate
ORDER BY r.PossibleDate;
I always get an error that GStart is not contained in either an aggregate function or the GROUP BY clause.
Simply JOIN the aggregate level to unit level which can be facilitated with a CTE. Run this entire statement below including WITH clause.
WITH agg AS (
SELECT CONVERT(VARCHAR(10), r.PossibleDate, 120) as [Date],
SUM(ISNULL(r.ElecCheckIn, 0)) as CheckIn,
SUM(ISNULL(r.ElecCheckOut, 0)) as CheckOut,
SUM(ISNULL(r.JPAmount, 0)) as AttAmountJP,
SUM(ISNULL(r.MeteredAttAmountCC, 0)) as AttAmountCC,
SUM(ISNULL(r.MeteredMachAmount, 0)) as MachAmount,
SUM(ISNULL(r.MeteredAttAmount, 0)) as AttAmount,
SUM(ISNULL(r.ElecCheckIn, 0) -
ISNULL(r.ElecCheckOut, 0) -
ISNULL(r.JPAmount, 0) -
ISNULL(r.MeteredAttAmountCC, 0) -
ISNULL(r.MeteredMachAmount, 0) -
ISNULL(r.MeteredAttAmount, 0)) as NetWin
FROM dbo.CDS_TableA sm (NOLOCK)
INNER JOIN dbo.bb_tableB st (NOLOCK)
ON sm.TableB_Id =s t.SlotB_Id
AND sm.TableBRevision=st.TabelBRevision
INNER JOIN dbo.TableC r (NOLOCK)
ON sm.TableA_ID= r.TableA_ID
AND r.PossibleDate BETWEEN '{dtStart.Value.ToString("yyyy-MM-dd")} 00:00:00'
AND '{dtEnd.Value.ToString("yyyy-MM-dd")} 23:59:59'
AND r.Period_ID=4
INNER JOIN dbo.BB_TableD rh (NOLOCK)
ON sm.TableA_ID=rh.TableA_ID
AND r.PossibleDate=rh.PossibleDate
AND sm.Revision=rh.Revision
WHERE sm.OnFloorFlag = 1
AND sm.Calc_ID NOT IN (2,5)
GROUP BY r.PossibleDate
)
SELECT r.GStart as GStart, agg.* --- ADD OTHER r FIELDS
FROM dbo.TableC r
INNER JOIN agg ON CONVERT(VARCHAR(10), r.PossibleDate, 120) = agg.[Date]
ORDER BY r.PossibleDate
Aside: While I know nothing of vb.net, I do know running SQL at application layer and your concatenation of dates above should be parameterized values which is a programming industry best practice. See How do I create a parameterized SQL query? Why Should I? Also, use NOLOCK with caution.

Not able to apply join in sql query

i want to add the join "JOIN {User}
ON {Deck}.[CreatedBy] = {User}.[Id]" in following query. I tried many combination but not succeeded. i want to fetch Name from User table
SELECT #CampaignQueryFilterString AS [Selected],
{Deck}.[Id], {Deck}.[Name],
{User}.[Name],
{Deck}.[TableOfContentId], {Deck}.[CreatedBy],
{Deck}.[LastModifiedOn], {Deck}.[ExpiryDate],s1.[Count]
FROM {Deck}
JOIN
(
SELECT {Deck}.[Id],
LISTAGG( {TagValue}.[Id], ',' ) WITHIN GROUP (ORDER BY {TagValue}.[TagCategoryId] ) AS Tags
FROM {Deck}
JOIN {AssociatedDeckTags}
ON {AssociatedDeckTags}.[DeckId] = {Deck}.[Id]
JOIN {TagValue}
ON {AssociatedDeckTags}.[TagValueId] = {TagValue}.[Id]
WHERE {Deck}.[IsPublished] = 1
AND {Deck}.[IsActive] = 1 AND {Deck}.[ReplacedByDeckId] IS NULL
AND {Deck}.[TableOfContentId] IN #TableOfContentIdFilterString
AND {Deck}.[ContentFileTypeId] IN #AllowedContentType
AND {Deck}.[ExpiryDate] > SYSDATE
GROUP BY {Deck}.[Id]
) DeckView
ON {Deck}.[Id] = DeckView.Id
JOIN(
SELECT COUNT(*) AS [Count], {DeckGroup}.[DeckId] AS [S1DeckId]
FROM {DeckGroup}
JOIN {Slide} ON {Slide}.[DeckGroupId] = {DeckGroup}.[Id]
GROUP BY {DeckGroup}.[DeckId]
) s1 ON s1.[S1DeckId] = {Deck}.[Id]
#RegexString
#SearchFilter
#CreatedBy
GROUP BY
{Deck}.[Id], {Deck}.[Name], {Deck}.[TableOfContentId],
{Deck}.[CreatedOn], {Deck}.[CreatedBy],
{Deck}.[LastModifiedOn], {Deck}.[ExpiryDate],
{Deck}.[NumOfPreviews], {Deck}.[NumOfDownloads],s1.[Count]
ORDER BY #Orderby
Looks like simply join, please try the below query (using your notation). I added left join and in group by clause - {User}.[Name]. Optionally you can use some aggregating function for {User}.[Name] - max(), listagg() and remove it from group by clause.
SELECT #CampaignQueryFilterString AS [Selected],
{Deck}.[Id], {Deck}.[Name], {User}.[Name], {Deck}.[TableOfContentId],
{Deck}.[CreatedBy], {Deck}.[LastModifiedOn], {Deck}.[ExpiryDate], s1.[Count]
FROM {Deck}
JOIN DeckView ON {Deck}.[Id] = DeckView.Id -- subquery1
JOIN s1 ON s1.[S1DeckId] = {Deck}.[Id] -- subquery2
LEFT JOIN {User} ON {Deck}.[CreatedBy] = {User}.[Id] -- <-- add join here
#RegexString
#SearchFilter
#CreatedBy
GROUP BY
{Deck}.[Id], {Deck}.[Name], {User}.[Name], -- <-- add column here
{Deck}.[TableOfContentId], {Deck}.[CreatedOn],
{Deck}.[CreatedBy], {Deck}.[LastModifiedOn],
{Deck}.[ExpiryDate], {Deck}.[NumOfPreviews],
{Deck}.[NumOfDownloads], s1.[Count]
ORDER BY #Orderby
You didn't show your tries, so we don't know if there was an error or undesired result. With this form of question that is all I can help. Also USER is one of Oracle Reserved Words, it's better to avoid using it as alias, variable name etc.

The type "geography" is not comparable. It cannot be used in the GROUP BY clause

I have a query like this :
SELECT WorkId,
RegisterDate , Location
FROM (
SELECT dbo.[Work].WorkId ,
dbo.[Work].RegisterDate , dbo.Look.Location
FROM dbo.Municipality INNER JOIN
dbo.[Work] ON dbo.Municipality .Municipality Id = dbo.[Work].MunicipalityWorkId INNER JOIN
dbo.Look ON dbo.[Work].LookWorkId = dbo.Look.LookId
WHERE (dbo.Look.Location IS NOT NULL) AND Type= 1
) E
GROUP BY WorkId ,RegisterDate , Location
And I get this error :
The type "geography" is not comparable. It cannot be used in the GROUP
BY clause.
I need to add Location to Group By, because I need to display Location in the database. What is the solution for this situation? Thanks.
Here is one way.
Convert the Location to text using Stastext and use it Group by. Then convert it back to geo in Select using STGeomFromText
SELECT WorkId,
RegisterDate,
geography::STGeomFromText(Location.STAsText(), 4326)
FROM (SELECT dbo.[Work].WorkId,
dbo.[Work].RegisterDate,
dbo.Look.Location
FROM dbo.Municipality
INNER JOIN dbo.[Work]
ON dbo.Municipality.MunicipalityId = dbo.[Work].MunicipalityWorkId
INNER JOIN dbo.Look
ON dbo.[Work].LookWorkId = dbo.Look.LookId
WHERE ( dbo.Look.Location IS NOT NULL )
AND Type = 1) E
GROUP BY WorkId,
RegisterDate,
Location.STAsText()
Referred from this answer
Note : The geography function are CASE sensitive it should be used as it is
STGeomFromText
STAsText
You an not using aggregate function like sum or count so you could avoid group by and order by
SELECT
WorkId
, RegisterDate
, Location
FROM (
SELECT
dbo.[Work].WorkId
, dbo.[Work].RegisterDate
, dbo.Look.Location
FROM dbo.Municipality
INNER JOIN dbo.[Work] ON dbo.Municipality.Municipality Id = dbo.[Work].MunicipalityWorkId
INNER JOIN dbo.Look ON dbo.[Work].LookWorkId = dbo.Look.LookId
WHERE (dbo.Look.Location IS NOT NULL) AND Type= 1
ORDER BY WorkId ,RegisterDate , Location
) E

Using results of JOIN statement to join on the results of another JOIN statement

I have the following 2 Join Statements:
--Get Total Hrs
DECLARE #BeginDate datetime, #EndDate datetime
set #BeginDate = '01-01-2013'
set #EndDate = '12-31-2013'
BEGIN
SELECT F.Type, E.Product, SUM(F.Hours * E.Amount) AS 'Total Hours'
FROM Hours H
INNER JOIN Equipment E
ON F.SN = E.SN
WHERE (F.Date BETWEEN #BeginDate AND #EndDate)
GROUP BY F.Type, E.Product
ORDER BY Product ASC
END
--Get Number of Unscheduled Removals
DECLARE #BeginDate1 datetime, #EndDate1 datetime
set #BeginDate1 = '01-01-2013'
set #EndDate1 = '12-31-2013'
BEGIN
SELECT LEFT(dbo.fn_GetPartName(R.PartID),CHARINDEX('-',dbo.fn_GetPartName(R.PartID), 1) - 1) AS 'Part No',
Count(s.status) AS NumberUnscheduledRemovals
FROM Repair R
INNER JOIN Conversion C
ON R.Performed = C.Performed
AND R.Confirmed = C.Confirmed
INNER JOIN Status S
ON C.StatusID = S.StatusID
WHERE (R.Received BETWEEN #BeginDate1 AND #EndDate1)
AND (S.Status = 'UNSCHEDULED')
GROUP BY LEFT(dbo.fn_GetPartName(R.PartID),CHARINDEX('-',dbo.fn_GetPartName(R.PartID), 1) - 1)
ORDER BY LEFT(dbo.fn_GetPartName(R.PartID),CHARINDEX('-',dbo.fn_GetPartName(R.PartID), 1) - 1) ASC
END
Both queries have results including part numbers (these have the same values). I want to INNER JOIN the results from both queries on the resulting part numbers. have been trying for a while but cant seem to get the syntax right to do it.
Use a temp table using CREATE TABLE #TempPartNum1 & #TempPartNum2.
Grab all the relevant data from the first two queries and put them in the temp tables, then join the temp tables.
You could also use a CTE ("Common Table Expression"):
;WITH QueryOne AS (
... put your first query here
), QueryTwo AS (
... put your second query here
) SELECT blah blah blah
FROM QueryOne INNER JOIN QueryTwo ON foo = bar
CTEs are very handy for things like this.

How to replace a NULL when a COUNT(*) returns NULL in DB2

I have a query:
SELECT A.AHSHMT AS SHIPMENT, A.AHVNAM AS VENDOR_NAME, D.UNITS_SHIPPED, D.ADPON AS PO, B.NUMBER_OF_CASES_ON_TRANSIT, C.NUMBER_OF_CASES_RECEIVED FROM AHASNF00 A
INNER JOIN (SELECT IDSHMT, COUNT(*) AS NUMBER_OF_CASES_ON_TRANSIT FROM IDCASE00 WHERE IDSTAT = '01' GROUP BY IDSHMT) B
ON (A.AHSHMT = B.IDSHMT)
LEFT JOIN (SELECT IDSHMT, (COUNT(*) AS NUMBER_OF_CASES_RECEIVED FROM IDCASE00 WHERE IDSTAT = '10' GROUP BY IDSHMT) C
ON (A.AHSHMT = C.IDSHMT)
INNER JOIN (SELECT ADSHMT, ADPON, SUM(ADUNSH) AS UNITS_SHIPPED FROM ADASNF00 GROUP BY ADSHMT, ADPON) D
ON (A.AHSHMT = D.ADSHMT)
WHERE A.AHSHMT = '540041134';
On the first JOIN statement I have a COUNT(*), on this count sometimes I will get NULL. I need to replace this with a "0-zero", I know think I know how to do it in SQL
ISNULL(COUNT(*), 0)
But this doesn't work for DB2, how can I accomplish this? All your help is really appreciate it.
Wrap a COALESCE around each of the nullable totals in your SELECT list:
SELECT A.AHSHMT AS SHIPMENT,
A.AHVNAM AS VENDOR_NAME,
COALESCE( D.UNITS_SHIPPED, 0 ) AS UNITS_SHIPPED,
D.ADPON AS PO,
COALESCE( B.NUMBER_OF_CASES_ON_TRANSIT, 0 ) AS NUMBER_OF_CASES_ON_TRANSIT,
COALESCE( C.NUMBER_OF_CASES_RECEIVED, 0 ) AS NUMBER_OF_CASES_RECEIVED
FROM ...
The inner joins you're using for expressions B and D mean that you will only receive rows from A that have one or more cases in transit (expression B) and have one or more POs in expression D. Is that the way you want your query to work?
Instead of using ISNULL(COUNT(*), 0),
try using COALESCE(COUNT(*),0)
use IFNULL(COUNT(*), 0) for DB2