3 Count functions returning the same value - sql

So i have this query:
SELECT Posts.[Post ID], Count(Comments.[Comment ID]) AS [CountOfComment ID],
Count(Shares.[Share ID]) AS [CountOfShare ID],
Count(Likes.[Like ID]) AS [CountOfLike ID]
FROM ((Posts INNER JOIN Likes ON Posts.[Post ID] = Likes.[Post ID])
INNER JOIN Shares ON Posts.[Post ID] = Shares.[Post ID])
INNER JOIN Comments ON Posts.[Post ID] = Comments.[Post ID]
WHERE posts.[post id] = 5
GROUP BY Posts.[Post ID];
Which should return the results:
post.[post id]= 5
CountofcommentID= 4
CountofShareID = 5
CountofLikeID = 15
But instead i get post.[post id] = 5, and the rest =300.
I know that this is because it is counting up all the permutations of all the IDs, but how do i change that to get the results that i want?

You are misunderstanding what COUNT() does. It counts the number of non-NULL values. The proper solution is to use COUNT(DISTINCT). Because MS-Access does not support COUNT(DISTINCT) you might interpret that as "get a functional database".
Inside MS Access, you can do this using subqueries of some type. Here is one method:
SELECT p.[Post ID],
(SELECT COUNT(*)
FROM (SELECT DISTINCT [Post Id], [Comment ID]
FROM Comments as c
) as c
WHERE c.[Post Id] = p.[Post Id]
) as [CountOfComment ID],
(SELECT COUNT(*)
FROM (SELECT DISTINCT [Post Id], [Share ID]
FROM Shares as s
) as s
WHERE s.[Post Id] = p.[Post Id]
) as [CountOfShare ID],
(SELECT COUNT(*)
FROM (SELECT DISTINCT [Post Id], [Like ID]
FROM Likes as l
) as l
WHERE l.[Post Id] = p.[Post Id]
) as [CountOfLikes ID]
FROM Posts as p
WHERE p.[post id] = 5
GROUP BY p.[Post ID];

When you join, you get a row for every combination. Therefore, count does count every row, which has value different from null. So, what you want is to count DISTINCT ids. I.e.:
SELECT Posts.[Post ID], Count(DISTINCT Comments.[Comment ID]) AS [CountOfComment ID],
Count(DISTINCT Shares.[Share ID]) AS [CountOfShare ID],
Count(DISTINCT Likes.[Like ID]) AS [CountOfLike ID]
FROM ((Posts INNER JOIN Likes ON Posts.[Post ID] = Likes.[Post ID])
INNER JOIN Shares ON Posts.[Post ID] = Shares.[Post ID])
INNER JOIN Comments ON Posts.[Post ID] = Comments.[Post ID]
WHERE posts.[post id] = 5
GROUP BY Posts.[Post ID];
If the system does not support this syntax, you might need to do subqueries:
Select Posts.[Post ID],
(Select count([Share ID]) from Shares where Posts.[Post ID] = Shares.[Post ID]) as SharesCount
FROM Posts
WHERE posts.[post id] = 5;

Related

Error: The order by clause is invalid in views

I'm getting the following error in SQL Server 14 where I am trying to build a view based on 4 tables
The ORDER BY clause is invalid in views, inline functions, derived
tables, subqueries, and common table expressions, unless TOP, OFFSET
or FOR XML is also specified
How do I fix the below so I don't get this error?
SELECT dbo.PS_Proj.[Project ID],
case
when PS_Proj.[City Name] is not null then concat(PS_Proj.[City Name], ' ', PS_Proj.[State])
when PS_Billing.[Location] is not null then max(PS_Billing.[Location])
when PS_Time.[Labor Location ID] is not null then max(PS_Location.[Labor Location Name])
else null
end AS [Location]
FROM dbo.PS_Location RIGHT OUTER JOIN
dbo.PS_Time ON dbo.PS_Location.[Labor Location ID] = dbo.PS_Time.[Labor Location ID] RIGHT OUTER JOIN
dbo.PS_Proj ON dbo.PS_Time.[Project ID] = dbo.PS_Proj.[Project ID] LEFT OUTER JOIN
dbo.PS_Billing ON dbo.PS_Proj.[Project ID] = dbo.PS_Billing.[Project ID]
ORDER BY PS_Billing.[T/S Date], PS_Time.[Date]
I'd like for the most recent PS_Billing location to show and if that is null for the most recent PS_Time location to show.
Create your view without Order By. Remove ORDER BY PS_Billing.[T/S Date], PS_Time.[Date] And add them to your SELECT.
SELECT
PS_Billing.[T/S Date] AS TsDate,
PS_Time.[Date] AS Date,
dbo.PS_Proj.[Project ID],
case
when PS_Proj.[City Name] is not null then concat(PS_Proj.[City Name], ' ', PS_Proj.[State])
when PS_Billing.[Location] is not null then max(PS_Billing.[Location])
when PS_Time.[Labor Location ID] is not null then max(PS_Location.[Labor Location Name])
else null
end AS [Location]
FROM dbo.PS_Location RIGHT OUTER JOIN
dbo.PS_Time ON dbo.PS_Location.[Labor Location ID] = dbo.PS_Time.[Labor Location ID] RIGHT OUTER JOIN
dbo.PS_Proj ON dbo.PS_Time.[Project ID] = dbo.PS_Proj.[Project ID] LEFT OUTER JOIN
dbo.PS_Billing ON dbo.PS_Proj.[Project ID] = dbo.PS_Billing.[Project ID]
And select from your view with order by line
Select FROM YOUR_VIEW_NAME as V
Order By V.TsDate, V.Date
You have some aggregate data inside your case expression and you don't have a group by. I am making some guesses here. Also, you should get in the habit of using aliases, it makes things a lot simpler. I would also suggest not putting spaces in your column names as it forces you to constantly wrap columns in square brackets.
Something like this should get you close.
SELECT p.[Project ID]
, MAX(case
when p.[City Name] is not null then concat(p.[City Name], ' ', p.[State])
when b.[Location] is not null then b.[Location]
when t.[Labor Location ID] is not null then l.[Labor Location Name]
else null
end) AS [Location]
FROM dbo.PS_Location l
RIGHT OUTER JOIN dbo.PS_Time t ON l.[Labor Location ID] = t.[Labor Location ID]
RIGHT OUTER JOIN dbo.PS_Proj p ON t.[Project ID] = p.[Project ID]
LEFT OUTER JOIN dbo.PS_Billing b ON p.[Project ID] = b.[Project ID]
GROUP BY p.[Project ID]

SQL Server Query: How do I get maximum counts for specific groupings?

We have a table with the following information:
Account ID, Touch Number, Type, Touch Date, and Stage (ranked 1-3, 1 if touch number < 50, 2 if 51-100, 3 if > 100).
Screenshot from table
I am looking to write a query that captures the type with the most touches at each stage for each account, looking something like this:
Output I am looking to receive
Here is the current query I wrote that is not working for me:
`SELECT distinct
a.[Account ID],
a.Stage,
bb.Stage1TopType,
bb.TypeCount_1,
c.Stage2TopType,
c.TypeCount_2,
d.Stage3TopType,
d.TypeCount_3
FROM SFAX.dbo.LinearTest as a
--STAGE 1
LEFT JOIN
(
SELECT
a.[Account ID],
a.Type as Stage1TopType,
Max(b.TouchCount) as TypeCount_1
FROM SFAX.dbo.LinearTest as a
LEFT JOIN
(
SELECT
[Account ID],
Type,
COUNT(TouchNumber) as TouchCount
FROM SFAX.dbo.LinearTest
WHERE Stage = 1
GROUP BY [Account ID], Type
) as b on a.[Account ID] = b.[Account ID]
WHERE a.Stage = 1
GROUP BY a.[Account ID], a.Type
) as bb on a.[Account ID] = bb.[Account ID]
--STAGE 2
LEFT JOIN
(
SELECT
a.[Account ID],
a.Type as Stage2TopType,
Max(b.TouchCount) as TypeCount_2
FROM SFAX.dbo.LinearTest as a
LEFT JOIN
(
SELECT
[Account ID],
Type,
COUNT(TouchNumber) as TouchCount
FROM SFAX.dbo.LinearTest
WHERE Stage = 2
GROUP BY [Account ID], Type
) as b on a.[Account ID] = b.[Account ID]
WHERE a.Stage = 2
GROUP BY a.[Account ID], a.Type
) as c on a.[Account ID] = c.[Account ID]
--STAGE 3
LEFT JOIN
(
SELECT
a.[Account ID],
a.Type as Stage3TopType,
Max(b.TouchCount) as TypeCount_3
FROM SFAX.dbo.LinearTest as a
LEFT JOIN
(
SELECT
[Account ID],
Type,
COUNT(TouchNumber) as TouchCount
FROM SFAX.dbo.LinearTest
WHERE Stage = 3
GROUP BY [Account ID], Type
) as b on a.[Account ID] = b.[Account ID]
WHERE a.Stage = 3
GROUP BY a.[Account ID], a.Type
) as d on a.[Account ID] = d.[Account ID]
`
Please let me know if you have any suggestions on how I can receive my desired output.
I believe a simple ROW NUMBER window function should be enough.
;WITH MostTouchesByAccountStage AS
(
SELECT
T.[Account ID],
T.Stage,
T.TouchNumber,
T.Type,
T.TouchDate,
Ranking = ROW_NUMBER() OVER ( -- Generate a ranking
PARTITION BY
T.[Account ID], -- That will reset with each different value of Account and Stage
T.Stage
ORDER BY
T.TouchNumber DESC) -- And is ordered by TouchNumber descendently
FROM
YourTable AS T
)
SELECT
T.*
FROM
MostTouchesByAccountStage AS T
WHERE
T.Ranking = 1
What I am looking for is the Type that appears the greatest number of
times in each stage.
You will need to perform the count first, then decide which of these has the highest value. Below you will see the count performed in a "derived table", then row_number() is used to assign a value of 1 to the highest count, and finally we only return te rows with that value of 1
SELECT
[Account ID]
, Stage
, TouchNumber
, Type
, TouchDate
, type_count
FROM (
SELECT
[Account ID]
, Stage
, TouchNumber
, Type
, TouchDate
, type_count
, ROW_NUMBER() OVER (PARTITION BY [Account ID], Stage ORDER BY type_count DESC, Type) AS rn
FROM (
SELECT
[Account ID]
, Stage
, TouchNumber
, Type
, TouchDate
, COUNT( * ) OVER (PARTITION BY [Account ID], Stage, Type) AS type_count
FROM YourTable AS T
) sq
) d
WHERE rn = 1
ORDER BY
[Account ID]
, Stage
nb. there might be more than one row with the same high value but only one row can be returned, if you want more then one row with for a tie use dense_rank() instead

Ms Access Delete Query with Join and Where Clause

I have 2 tables (tblTrainingElements and tblCourses).
tblTrainingElements has the following fields:
([Training Element ID], [Course ID], [Title],[Duration (min)],[Patient],[Status],[Description], [Comments],[Site],[ElementSeq])
tblCourses has the following relevant fields:
[Course ID], [App ID]
I need to delete all records in tblTrainingElements WHERE the [App ID] from tblCourses = "CAD". I need to join the tables on the [Course ID] field. Below is the SQL statement that I tried to use. I keep getting the error message "Please specify which table you would like to delete from"
DELETE tblCourses.[Course Name], tblCourses.[App ID], tblTrainingElements.[Training Element ID], tblTrainingElements.[Course ID], tblTrainingElements.Title, tblTrainingElements.[Duration (min)], tblTrainingElements.Patient, tblTrainingElements.Status, tblTrainingElements.Description, tblTrainingElements.Comments, tblTrainingElements.Site, tblTrainingElements.ElementSeq
FROM tblCourses INNER JOIN tblTrainingElements ON tblCourses.[Course ID] = tblTrainingElements.[Course ID]
WHERE (((tblCourses.[App ID])="CAD"))
Delete records only in tblTrainingElements table:
DELETE *
FROM tblTrainingElements
WHERE tblTrainingElements.[Course ID] IN (SELECT tblCourses.[Course ID] FROM tblCourses WHERE tblCourses.[App ID]="CAD" );
DELETE tc
FROM tblCourses tc
INNER JOIN tblTrainingElements tte ON tc.[Course ID] = tte.[Course ID]
WHERE (((tc.[App ID])="CAD"))

SQL: How do I show all Items in Inventory and Sum sales of items sold in a time period?

I am trying to find all items on hand from #Supplier_ID and summarize any sales since #Begin_Date. What is returned are all items on hand that have never been sold and those sold since #Begin_Date. Items on hand that were sold before #Begin_Date are excluded from the results. How do I fix that?
I am using SQL Server 2012 and SSRS v3.
SELECT DISTINCT
inventory_supplier.supplier_id AS [Supp ID],
address.name AS Supplier,
inv_loc.location_id AS [Inventory Loc ID],
inv_mast.item_id AS [Item ID],
inv_mast.item_desc AS [Item Desc],
inv_loc.qty_on_hand AS QOH,
inv_loc.moving_average_cost AS MAC,
invoice_line.qty_shipped,
invoice_hdr.customer_id AS [Customer ID],
invoice_hdr.bill2_name AS Customer,
oe_line.source_loc_id AS [Sales Source Loc]
FROM
inventory_supplier
INNER JOIN
inv_mast ON inventory_supplier.inv_mast_uid = inv_mast.inv_mast_uid
INNER JOIN
address ON inventory_supplier.supplier_id = address.id
FULL OUTER JOIN
invoice_line ON inv_mast.inv_mast_uid = invoice_line.inv_mast_uid
FULL OUTER JOIN
inv_loc ON inv_mast.inv_mast_uid = inv_loc.inv_mast_uid
FULL OUTER JOIN
invoice_hdr ON invoice_line.invoice_no = invoice_hdr.invoice_no
FULL OUTER JOIN
oe_line ON invoice_hdr.order_no = oe_line.order_no
AND invoice_line.inv_mast_uid = oe_line.inv_mast_uid
WHERE
(inventory_supplier.supplier_id = #Supplier_ID)
AND (invoice_hdr.invoice_date >= #Begin_Date
OR invoice_hdr.invoice_date IS NULL)
AND (inv_loc.qty_on_hand > 0)
ORDER BY
[Item ID], [Inventory Loc ID], [Customer ID], [Sales Source Loc]
You could move your invoice_hdr.invoice_date >= #Begin_Date to your join statement
FULL OUTER JOIN
invoice_hdr ON invoice_line.invoice_no = invoice_hdr.invoice_no
AND invoice_hdr.invoice_date >= #Begin_Date
Don't see a lot of FULL OUTER JOINs. Sure you don't want LEFT JOIN here?
You might want to separate out the Invoice information from the Inventory information into a subquery, and LEFT JOIN to the Invoice information.
SELECT DISTINCT
inventory_supplier.supplier_id AS [Supp ID],
address.name AS Supplier,
inv_loc.location_id AS [Inventory Loc ID],
inv_mast.item_id AS [Item ID],
inv_mast.item_desc AS [Item Desc],
inv_loc.qty_on_hand AS QOH,
inv_loc.moving_average_cost AS MAC,
invoices.qty_shipped,
invoices.customer_id AS [Customer ID],
invoices.bill2_name AS Customer,
invoices.source_loc_id AS [Sales Source Loc]
FROM
inventory_supplier
INNER JOIN
inv_mast ON inventory_supplier.inv_mast_uid = inv_mast.inv_mast_uid
INNER JOIN
address ON inventory_supplier.supplier_id = address.id
INNER JOIN
inv_loc ON inv_mast.inv_mast_uid = inv_loc.inv_mast_uid
LEFT OUTER JOIN
(SELECT
invoice_line.inv_mast_uid,
invoice_line.qty_shipped,
invoice_hdr.customer_id,
invoice_hdr.bill2_name,
oe_line.source_loc_id
FROM
invoice_line
INNER JOIN
invoice_hdr ON invoice_line.invoice_no = invoice_hdr.invoice_no
INNER JOIN
oe_line ON invoice_hdr.order_no = oe_line.order_no
AND invoice_line.inv_mast_uid = oe_line.inv_mast_uid
WHERE
invoice_hdr.invoice_date >= #Begin_Date
) invoices ON invoices.inv_mast_uid = inv_mast.inv_mast_uid
WHERE
inventory_supplier.supplier_id = #Supplier_ID
AND inv_loc.qty_on_hand > 0
ORDER BY
[Item ID], [Inventory Loc ID], [Customer ID], [Sales Source Loc]
Try changing
WHERE
(inventory_supplier.supplier_id = #Supplier_ID)
AND (invoice_hdr.invoice_date >= #Begin_Date
OR invoice_hdr.invoice_date IS NULL)
AND (inv_loc.qty_on_hand > 0)
to
WHERE
(inventory_supplier.supplier_id = #Supplier_ID)
AND (invoice_hdr.invoice_date >= #Begin_Date)
AND (inv_loc.qty_on_hand > 0)
The problem is because you are including dates, invoice_hdr.invoice_date that are NULL in the WHERE clause. Simply remove it:
WHERE
(inventory_supplier.supplier_id = #Supplier_ID)
AND (invoice_hdr.invoice_date >= #Begin_Date)
AND (inv_loc.qty_on_hand > 0)

SQL joins and group by with 3 separate tables

I have two separate queries here that I need to make into one query, I'll post the queries, then try to explain what I'm trying to do.
SELECT Distinct I.ITMCDE, V.VNDRCDE, V.VNAME
FROM (SELECT RIGHT(Items.[Item Number], 3) as ITMCDE FROM Items) I,
(SELECT LEFT(Vendors.[Vendor ID], 3) as VNDRCDE,
Vendors.[Vendor Name] as VNAME
FROM Vendors) V
WHERE I.ITMCDE = V.VNDRCDE
In this first one, I simply match up the vendor code with the item code, to get the vendor name that produces the item.
SELECT DISTINCT (Items.[Item Description]), ItemQuantities.[QTY Available],
Items.[Selling U Of M], Items.[Item Number]
FROM ItemQuantities
INNER JOIN Items ON ItemQuantities.[Item Number] = Items.[Item Number]
WHERE Items.[Item Number] LIKE 'WH%'
AND Items.[Item Number] NOT LIKE '%RMW'
In this second one I'm selecting the item description, quantity available from two separate tables (quantity available is in a different table, match them up using the item number)
As you can see, the only correlation between the three tables is the item number, and not even that in the vendors table. The last three characters of the item number correlate with the first three characters of the vendor id... I did not design this setup. I'm just trying to work with it now.
How do I join these two statements into one single statement that will give me the vendor name, item description, Unit of Measure (Selling U of M), and item quantity where the item description is unique?
I think this should work:
SELECT DISTINCT Items.[Item Description],
ItemQuantities.[QTY Available],
Items.[Selling U Of M],
Items.[Item Number],
V.VNAME
FROM ItemQuantities
INNER JOIN Items ON ItemQuantities.[Item Number] = Items.[Item Number]
INNER JOIN Vendors ON
RIGHT(Items.[Item Number], 3) = LEFT(Vendors.[Vendor ID], 3)
WHERE Items.[Item Number] LIKE 'WH%'
AND Items.[Item Number] NOT LIKE '%RMW'
You can join it in:
SELECT DISTINCT (i.[Item Description]), iq.[QTY Available],
i.[Selling U Of M], i.[Item Number],
V.VNDRCDE, V.VNAME
FROM ItemQuantities iq INNER JOIN
Items i
ON iq.[Item Number] = i.[Item Number] left outer join
Vendors v
on LEFT(v.[Vendor ID], 3) = RIGHT(i.[Item Number], 3)
WHERE i.[Item Number] LIKE 'WH%' and
i.[Item Number] NOT LIKE '%RMW'
I am not sure if you intend anything special with the parentheses around i.[Item Description]. The distinct keyword applies to the entire row.
If you want distinct only on the description, then you need to use group by. Something like:
SELECT i.[Item Description],
max(iq.[QTY Available]),
max(i.[Selling U Of M]), max(i.[Item Number]),
max(V.VNDRCDE), max(V.VNAME)
FROM ItemQuantities iq INNER JOIN
Items i
ON iq.[Item Number] = i.[Item Number] left outer join
Vendors v
on LEFT(v.[Vendor ID], 3) = RIGHT(i.[Item Number], 3)
WHERE i.[Item Number] LIKE 'WH%' and
i.[Item Number] NOT LIKE '%RMW'
group by i.[Item Description])
The max() will return the maximum value. If all are the same, then this is a good way to get an "arbitrary" value.
#sgeddes Thank you.
This is what I came up with
SELECT DISTINCT
Item.ITEMNMBR AS [Item Number],
Item.ITEMDESC AS [Item Description],
Item.ITMGEDSC AS [Item Category],
Item.SELNGUOM AS [Unit of Measure],
(SELECT VENDNAME FROM PM00200 WHERE (VENDORID = IV00103.VENDORID)) AS [Vendor Name],
(CASE WHEN Quan.QTYONHND > 0 THEN 'In Stock' ELSE 'Out of Stock' END) AS [Stock Status]
FROM IV00101 AS Item INNER JOIN
IV00102 AS Quan ON Item.ITEMNMBR = Quan.ITEMNMBR INNER JOIN
IV00103 ON Item.ITEMNMBR = IV00103.ITEMNMBR AND Quan.ITEMNMBR = IV00103.ITEMNMBR
WHERE (Item.ITEMNMBR LIKE 'WH%') AND (IV00103.VENDORID NOT LIKE '%MIL')