SQL - query to report MAX DATE results or NULL results - sql

I have an SQL database with 3 tables:
Customer record table, holding master data (each row is unique)
Customer notes, each note data/time stamped (there could be many notes per customer, or none at all)
Product table, showing which customer has bought which product
Table: tbl_Customer_Records
CustomerID---- Company Name-----Company Segment----- Promo Code
Table: tbl_Customer_Notes
CustomerID---- Note-----Created Date
Table: tbl_Customer_Products
CustomerID---- Product Category
What I want is to pull a list of customer records which includes the latest note only, so there are no duplicate lines if multiple notes exist. But I also want my report to include customer records if no notes exist at all. I've achieved the first part of this with a SELECT MAX function, and that works well, the problem is when I add the OR = NULL clause in the final line of code below. This doesn't work, and I can't figure out a solution.
Any suggestions will be greatly appreciated!
SELECT
[tbl_Customer_Records].[CustomerID],
[tbl_Customer_Records].[Company Name],
[tbl_Customer_Records].[Company Segment],
[tbl_Customer_Records].[Promo Code],
[tbl_Customer_Notes].[Note],
[tbl_Customer_Products].[Product Category]
FROM
tbl_Customer_Records
LEFT OUTER JOIN tbl_Customer_Notes
ON tbl_Customer_Records.CustomerID = tbl_Customer_Notes.CustomerID
LEFT OUTER JOIN tbl_Customer_Products
ON tbl_Customer_Records.CustomerID = tbl_Customer_Products.CustomerID
WHERE
[Product Category] in ('Nuts','Bolts','Screws','Spanners')
AND
[Created Date] in (SELECT MAX ([Created Date]) FROM tbl.Customer_Notes GROUP BY [CustomerID])
OR tbl_Customer_Note.Note is null

There're a few tricks to do this kind of query (row_number or join with grouped data), but I think most cleanest one in your case is to use outer apply:
select
cr.[CustomerID],
cr.[Company Name],
cr.[Company Segment],
cr.[Promo Code],
cn.[Note],
cp.[Product Category]
from tbl_Customer_Records as cr
left outer join tbl_Customer_Products as cp on cp.CustomerID = cr.CustomerID
outer apply (
select top 1
t.[Note]
from tbl_Customer_Notes as t
where t.[CustomerID] = cr.[CustomerID]
order by t.[Created_Date] desc
) as cn
where
cp.[Product Category] in ('Nuts','Bolts','Screws','Spanners')
Changed all clumsy table name.column name to alias.column name, I think it's much more readable this way.
Or:
select
cr.[CustomerID],
cr.[Company Name],
cr.[Company Segment],
cr.[Promo Code],
cn.[Note],
cp.[Product Category]
from tbl_Customer_Records as cr
left outer join tbl_Customer_Products as cp on cp.CustomerID = cr.CustomerID
left outer join tbl_Customer_Notes as cn on
cn.CustomerID = cr.CustomerID and
cn.[Created_Date] = (select max(t.[Created_Date]) from tbl_Customer_Notes as t where t.CustomerID = cr.CustomerID)
where
cp.[Product Category] in ('Nuts','Bolts','Screws','Spanners')

You can add your filter condition in ON predicate to preserve rows from left table and fetch only required matching rows from right table, from first LEFT OUTER JOIN operator. Following query should work:
SELECT
CR.[CustomerID],
CR.[Company_Name],
CR.[Company_Segment],
CR.[Promo_Code],
CN.[Note],
CP.[Product_Category]
FROM
tbl_Customer_Records CR
LEFT OUTER JOIN tbl_Customer_Notes CN
ON CR.CustomerID = CN.CustomerID AND CN.[Created_Date] in (SELECT MAX ([Created_Date])
FROM tbl_Customer_Notes
WHERE CR.CustomerID = tbl_Customer_Notes.CustomerID
GROUP BY [CustomerID])
LEFT OUTER JOIN tbl_Customer_Products CP
ON CR.CustomerID = CP.CustomerID
WHERE
[Product_Category] in ('Nuts','Bolts','Screws','Spanners')

Should work, tried with NULL values:
SELECT a.[CustomerID],
a.[Company Name],
a.[Company Segment],
a.[Promo Code],
a.[Note],
a.[Product Category]
FROM (
SELECT
cr.[CustomerID],
cr.[Company Name],
cr.[Company Segment],
cr.[Promo Code],
cn.[Note],
cp.[Product Category],
ROW_NUMBER() OVER(PARTITION BY cr.[CustomerID] ORDER BY cn.[Created Date] DESC) as rnk
FROM tbl_Customer_Records cr
LEFT JOIN tbl_Customer_Notes cn
ON cr.CustomerID = cn.CustomerID
LEFT JOIN tbl_Customer_Products cp
ON cr.CustomerID = cp.CustomerID
WHERE cp.[Product Category] in ('Nuts','Bolts','Screws','Spanners') )a
WHERE a.rnk = 1

try to use CustomerID not in (select CustomerID from tbl_Customer_Note)

Related

How to exclude duplicate values / NULL values SQL

I hope that someone from you will be able to help me as I got stuck with a (silly) condition logic.
I am joining 2 temp tables.
The target is to see the Jobs, Tracking numbers, etc. (all actions) in one row always for one Consignee (Consignee Ref). In other words, to show all actions/data per Consignee Ref.
The issue I got is duplacated values. The problem is that the Original Job (620X) field can have assigned Tracking_1 but it can be also null. Also the Second Job (629X) filed can have but do not need to have assigned the Tracking number/value. It can be also the case that both Tracking_1 and Tracking are NULL for one Consignee Ref.
When I would exclude Jobs where Tracking_1 or Tracking is NULL, then I will loose the Jobs that have no Tracking Numbers at all. So I used the code below, but then I am getting duplicates.
SELECT * FROM #Temp t1
INNER JOIN #Temp2 t2 on t1.[Consignee Ref] = t2.[Consignee Ref]
WHERE t1.[Tracking_1] is not null
ORDER BY [Original Job (620X)] asc
How can I make it that I will get rid of the unnecessary duplicates (an example marked in red)?
And at the same time not to loose those jobs that have no Tracking Numbers?
I hope it makes sense.
Thank you very much in advance for any advise!
This is the entire code:
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp;
SELECT distinct
cne.[Consignee Ref],
s.[Trial AWB] as [Original Job (620X)],
rl.[CarrierReference] as [Tracking_1],
CAST(st.[Sched Collection date] as date) AS [Collection Date],
CAST(st.[Act Del Date] as date) AS [Delivery Date],
s.[Clientaccountcode] as [Account Code]
into #Temp
FROM MKN_Reporting.dbo.shipment AS s WITH (NOLOCK)
LEFT JOIN MKN_Reporting.dbo.[Lookup Month By JN Tb] AS b WITH (NOLOCK) ON s.[id] = b.[JobId]
LEFT JOIN MKN_Reporting.dbo.[Lookup Branch currency Tb] AS c WITH (NOLOCK) ON b.BranchPrefix = c.[Branch Prefix]
LEFT JOIN MKN_Reporting.dbo.[Lookup Client group Tb] AS g WITH (NOLOCK) ON s.[Clientaccountcode] = g.[Customer A/c]
LEFT JOIN MKN_Reporting.dbo.[Chargeto] AS cg WITH (NOLOCK) ON s.[id] = cg.[jobid]
LEFT JOIN MKN_Reporting.dbo.[Shipper] AS sh WITH (NOLOCK) ON s.[id] = sh.[jobid]
LEFT JOIN MKN_Reporting.dbo.[Cnee] AS cne WITH (NOLOCK) ON s.[id] = cne.[jobid]
LEFT JOIN MKN_Reporting.dbo.[Lookup Country & Region Tb] AS r WITH (NOLOCK) ON sh.[Shipper Country code] = r.[Country Code]
LEFT JOIN MKN_Reporting.dbo.[Lookup Country & Region Tb] AS reg WITH (NOLOCK) ON cne.[Consignee Country code] = reg.[Country Code]
INNER JOIN MKN_Reporting.dbo.[Status] st ON COALESCE(s.ParentId, s.id) = st.Jobid
LEFT JOIN [CARRIERS_CHARGES] AS cc ON s.id = cc.JobId
LEFT JOIN [RouteLegs] as rl on cc.RouteLegId = rl.id
WHERE [Clientaccountcode] in ('US429', 'MI1091')
--AND rl.[CarrierReference] is not null
-------------------------------------------
IF OBJECT_ID('tempdb..#Temp2') IS NOT NULL
DROP TABLE #Temp2;
SELECT distinct
cne.[Consignee Ref],
s.[Study Number] as [Study_],
s.[Site Number] as [Site No_],
s.[Trial AWB] as [Second (629X)],
rl.[CarrierReference] as [Tracking],
CAST(st.[Sched Collection date] as date) AS [Collection Date],
CAST(st.[Act Del Date] as date) AS [Delivery Date],
s.[Clientaccountcode] as [Account Code]
into #Temp2
FROM MKN_Reporting.dbo.shipment AS s WITH (NOLOCK)
LEFT JOIN MKN_Reporting.dbo.[Lookup Month By JN Tb] AS b WITH (NOLOCK) ON s.[id] = b.[JobId]
LEFT JOIN MKN_Reporting.dbo.[Lookup Branch currency Tb] AS c WITH (NOLOCK) ON b.BranchPrefix = c.[Branch Prefix]
LEFT JOIN MKN_Reporting.dbo.[Lookup Client group Tb] AS g WITH (NOLOCK) ON s.[Clientaccountcode] = g.[Customer A/c]
LEFT JOIN MKN_Reporting.dbo.[Chargeto] AS cg WITH (NOLOCK) ON s.[id] = cg.[jobid]
LEFT JOIN MKN_Reporting.dbo.[Shipper] AS sh WITH (NOLOCK) ON s.[id] = sh.[jobid]
LEFT JOIN MKN_Reporting.dbo.[Cnee] AS cne WITH (NOLOCK) ON s.[id] = cne.[jobid]
LEFT JOIN MKN_Reporting.dbo.[Lookup Country & Region Tb] AS r WITH (NOLOCK) ON sh.[Shipper Country code] = r.[Country Code]
LEFT JOIN MKN_Reporting.dbo.[Lookup Country & Region Tb] AS reg WITH (NOLOCK) ON cne.[Consignee Country code] = reg.[Country Code]
INNER JOIN MKN_Reporting.dbo.[Status] st ON COALESCE(s.ParentId, s.id) = st.Jobid
LEFT JOIN [CARRIERS_CHARGES] AS cc ON s.id = cc.JobId
left JOIN [RouteLegs] as rl on cc.RouteLegId = rl.id
WHERE [Clientaccountcode] in ('US1598')
--AND rl.[CarrierReference] is not null
---------------------------------------------
SELECT * FROM #Temp t1
INNER JOIN #Temp2 t2 on t1.[Consignee Ref] = t2.[Consignee Ref]
WHERE t1.[Tracking_1] is not null
ORDER BY [Original Job (620X)] asc
I guess OP is looking for some kind of field aggregation for T2.
With SQL Server 2017+, like in PostgreSQL, you can now use STRING_AGG (same syntax than the Oracle LISTAGG.. "Why did they not use the same keyword...").
STRING_AGG will ignore null values from T2 and requires grouping if not alone. You have to add the other T1 and T2 required fields if any, but take care for T2 that it can imply duplicates and may need aggregation too...
Here is an example with your provided TSQL :
SELECT t1.[Consignee Ref], t1.[Original Job (620X)], t1.[Tracking_1],
STRING_AGG(t2.[Tracking],',') WITHIN GROUP (ORDER BY t2.[Tracking] ASC) Tracking_T2
FROM #Temp t1
INNER JOIN #Temp2 t2 on t1.[Consignee Ref] = t2.[Consignee Ref]
WHERE t1.[Tracking_1] is not null
GROUP BY t1.[Consignee Ref], t1.[Original Job (620X)], t1.[Tracking_1]
ORDER BY t1.[Original Job (620X)] asc
EDIT: For previous SQL Server release (before 2017), you can achieve string aggregation with XML query :
EDIT2: Adding EXISTS clause to convey the INNER JOIN from the initial query.
SELECT t1.[Consignee Ref], t1.[Original Job (620X)], t1.[Tracking_1],
STUFF((SELECT ', ' + t2.[Tracking]
FROM #Temp2 t2
WHERE t1.[Consignee Ref] = t2.[Consignee Ref]
ORDER BY t2.[Tracking]
FOR XML PATH('')), 1, 1, N'') Tracking_T2
FROM #Temp t1
WHERE t1.[Tracking_1] is not null
AND EXISTS (SELECT DISTINCT 1 FROM #Temp2 t2 WHERE t2.[Consignee Ref] = t1.[Consignee Ref])
GROUP BY t1.[Consignee Ref], t1.[Original Job (620X)], t1.[Tracking_1]
ORDER BY t1.[Original Job (620X)] asc

SQL server inner join not returning Description

Looking for some help trying to get some information out of my point of sale db. It is a MS Sql 13.0.4001.0 database
I have two tables. A "stock" table and a "stock UDF" table. They look a little like this:
The Stock Table lets call "ST" and the Stock UDF table "SU"
Stock Table has the following columns
SKU, Description, UDF1 ID, UDF2 ID,UDF3 ID,UDF4 ID
The Stock UDF table has the following columns
ID, Description
I want to create a query that returns a record but instead of the ID under the UDF1 ID column I want to get the description from the SU table.
A sample record in the ST currently looks like this
SKU, Description, UDF1 ID, UDF2 ID,UDF3 ID,UDF4 ID
1000 Orange 2 1 3 Null
The SU table looks like this
ID, Description
1 Fruit
2 Salads
3 Desserts
4 Vegetables
5 Raw
6 Cooked
I want to create a query that returns the following
SKU Description UDF1 UDF2 UDF3 UDF4
1000 Oranges Salads Fruit Desserts
Not sure how to do the inner join correctly.
Something like this:
select st.SKU, st.Description, st.[UDF1 Id], st.[UDF2 Id], st.[UDF3 Id], st.[UDF4 Id]
from [Stock] as st inner join [Stock UDF] as su on st.UDF1 ID = su.ID
But doesn't return what I want.
Thanks in advance.
You simply need to join to the table with the description multiple times, as follows. Joins are implicitly INNER JOIN, so it's not necessary to state that unless you want to. Note, however, that in this case you must use a LEFT OUTER join type, else where there is no match in the joined table, you will return no rows.
EDIT
I've added COALESCE() functions so that in the event a su.Decription field returns NULL, the query will actually output an empty string instead of the NULL. COALESCE() is a handy function that returns the first non-null value in its set of arguments.
SELECT st.SKU
,st.Description
,COALESCE(su1.Description, '') [UDF1 Desc]
,COALESCE(su2.Description, '') [UDF2 Desc]
,COALESCE(su3.Description, '') [UDF3 Desc]
,COALESCE(su4.Description, '') [UDF4 Desc]
FROM Stock st
LEFT JOIN [Stock UDF] su1 ON st.UDF1 = su1.ID
LEFT JOIN [Stock UDF] su2 ON st.UDF2 = su2.ID
LEFT JOIN [Stock UDF] su3 ON st.UDF3 = su3.ID
LEFT JOIN [Stock UDF] su4 ON st.UDF4 = su4.ID
You need to use left joins as you want stock descriptions even if the record is missing. Specifying join or inner join means that the record/field can't be null and return.
select
st.SKU, st.Description,
st.[UDF1 Id], ISNULL(su1.Description,''),
st.[UDF2 Id], ISNULL(su2.Description,''),
st.[UDF3 Id], ISNULL(su3.Description,''),
st.[UDF4 Id], ISNULL(su4.Description,'')
from [Stock] as st
LEFT join [Stock UDF] as su1 on st.[UDF1 Id] = su1.ID
LEFT join [Stock UDF] as su2 on st.[UDF2 Id] = su2.ID
LEFT join [Stock UDF] as su3 on st.[UDF3 Id] = su3.ID
LEFT join [Stock UDF] as su4 on st.[UDF4 Id] = su4.ID
As there are 4 foreign keys in the [Stock] table (UDF1 ID, UDF2 ID,UDF3 ID,UDF4 ID), you need to join [Stock UDF] table 4 times.
left join [Stock UDF] as su1 on st.UDF1 ID = su1.ID
left join [Stock UDF] as su2 on st.UDF2 ID = su2.ID
left join [Stock UDF] as su3 on st.UDF3 ID = su3.ID
left join [Stock UDF] as su4 on st.UDF4 ID = su4.ID
As there are null values consider using LEFT JOIN instead of INNER JOIN.

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 Query Second sum column squaring results

If I run 2 queries separately I get results like this..
select A.ACTNUMST, sum(B.EXTDCOST) as [IV Total]
from GL00105 A
INNER JOIN SEE30303 B on A.ACTINDX = B.IVIVINDX
group by A.ACTNUMST
Results -
Account No IV Total
2101-00-137 2033.60
4101-00-137 83765.86
6101-00-137 301984.23
Second Query
select A.ACTNUMST as [Account No], SUM(C.PERDBLNC) as [GL Total]
from GL00105 A
LEFT JOIN GL10110 C on A.ACTINDX = C.ACTINDX
group by A.ACTNUMST
Results -
Account No GL Total
2101-00-137 2033.60
4101-00-137 83765.86
6101-00-137 302656.23
I want to be able to join both results together to compare but I believe it is repeating the sum for each line in the GL total and then summing it again, it comes out with large numbers like -
select A.ACTNUMST as [Account No], sum(B.EXTDCOST) as [IV Total], SUM(C.PERDBLNC) as [GL Total]
from GL00105 A
INNER JOIN SEE30303 B on A.ACTINDX = B.IVIVINDX
LEFT JOIN GL10110 C on A.ACTINDX = C.ACTINDX
group by A.ACTNUMST
Results -
Account No IV Total GL Total
2101-00-137 2033.60 14235.20
4101-00-137 83765.86 116350696.20
6101-00-137 301984.23 1612897825.84
When it should be
Results -
Account No IV Total GL Total
2101-00-137 2033.60 2033.60
4101-00-137 83765.86 83765.86
6101-00-137 301984.23 302656.23
Please advise how to use the sum function to get the correct results.
Do you want something like this? Please check and comment if this is not what is expected.
Also please rectify any syntax errors.
SELECT T1.[Account No], T1.[IV Total], T2.[GL Total] FROM
(
select A.ACTNUMST as [Account No], sum(B.EXTDCOST) as [IV Total]
from GL00105 A
INNER JOIN SEE30303 B on A.ACTINDX = B.IVIVINDX
group by A.ACTNUMST
) T1
INNER JOIN
(
select A.ACTNUMST as [Account No], SUM(C.PERDBLNC) as [GL Total]
from GL00105 A
LEFT JOIN GL10110 C on A.ACTINDX = C.ACTINDX
group by A.ACTNUMST
) T2
ON T1.[Account No] = T2.[Account No]
Hope this helps
this can be done with the multiple cte also
with IVTotal as
(
select A.ACTNUMST, sum(B.EXTDCOST) as [IV Total]
from GL00105 A
INNER JOIN SEE30303 B
on A.ACTINDX = B.IVIVINDX
group by A.ACTNUMST
),
GLTotal as
(
select A.ACTNUMST as [Account No], SUM(C.PERDBLNC) as [GL Total]
from GL00105 A
LEFT JOIN GL10110 C
on A.ACTINDX = C.ACTINDX
group by A.ACTNUMST
)
select a.*,b.[GL Total]
from IVTotal a ,GLTotal b
where a.ACTNUMST = b.aCTNUMST

Filtering based on COUNT

I have a query that works really well. But I am trying to add a filter so if users_in_this_country is > 1. I know to add users_in_this_country > 1 to the WHERE. But if I add it inside the parenthesis it says invalid column and the same if I add it outside of the parenthesis. This is probably really dumb and easy, but what am I over looking? Thanks!
SELECT u.ContactName
,cu.[User ID]
,c.Name
,c.ID
,cu.[Foreign Table]
,count(*) OVER (PARTITION BY c.ID) AS user_in_this_country
FROM dbo.Country AS c
INNER JOIN dbo.CountryUser AS cu ON c.ID = cu.[Foreign ID]
INNER JOIN dbo.UserColder AS u ON cu.[User ID] = u.ID
WHERE EXISTS (
SELECT *
FROM CountryUser AS cu2
WHERE cu2.[Foreign ID] = cu.[Foreign ID]
AND cu2.[User ID] <> cu.[User ID]
AND cu.[Foreign Table] = 'Country')
The reason you can't refer to it in the WHERE clause is that its very meaning, for a given row, depends on what other rows satisfy the WHERE clause; so it would all be too circular.
The simplest fix is to wrap your entire query in SELECT * FROM ( ... ) t WHERE t.user_in_this_country > 1.
That said, it seems like your query already ensures that user_in_this_country > 1, by virtue of the EXISTS clause, which makes sure that there exists a different CountryUser record that belongs to the same Country and a different User. What am I missing?
With aggregate functions (like COUNT) used in conjuction with OVER clause you have to use a CTE or a subquery, like this
WITH CTE AS (
SELECT u.ContactName
,cu.[User ID]
,c.Name
,c.ID
,cu.[Foreign Table]
,count(*) OVER (PARTITION BY c.ID) AS user_in_this_country
FROM dbo.Country AS c
INNER JOIN dbo.CountryUser AS cu ON c.ID = cu.[Foreign ID]
INNER JOIN dbo.UserColder AS u ON cu.[User ID] = u.ID
WHERE EXISTS (
SELECT *
FROM CountryUser AS cu2
WHERE cu2.[Foreign ID] = cu.[Foreign ID]
AND cu2.[User ID] <> cu.[User ID]
AND cu.[Foreign Table] = 'Country')
)
SELECT *
FROM CTE
WHERE user_in_this_country > 1
Because "users_in_this_country" is not a column, it is an alias which is not valid in the scope of the WHERE clause. I 'm not familiar with "OVER" or PARTITION BY but, my guess is you'd have to do something like this:
WHERE blabla AND (count(*) OVER (PARTITION BY c.ID)) > 1