Filtering based on COUNT - sql

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

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

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]

Access Query and attributes

The setup is this: every location has many different accounts. Some are supplier only accounts, some are not. Each account has many bills associated with it.
I need to do one of two things:
Create a dynamic attribute in the location table that will tell me if the location is associated with an account (or many) that are supplier only. Should be a true/false attribute.
OR
Create a query that will return all the bills for all the locations that are associated with a supplier only account. I do not want a query that only returns bills from supplier only accounts.
Thanks for you help!
Here's the answer:
Location3PS Query:
SELECT DISTINCT Locations.Number
FROM Locations INNER JOIN Accounts ON Locations.Number = Accounts.[Location Num]
WHERE (((Accounts.[Supplier Only?])=True));
Final Query to Get Bills:
SELECT Bills.*, Location3PS.*
FROM
Location3PS INNER JOIN
(
Locations INNER JOIN
(
Accounts INNER JOIN Bills
ON Accounts.[Account Number] = Bills.[Account Number]
)
ON Locations.Number = Accounts.[Location Num]
)
ON Location3PS.Number = Locations.Number;
You can accomplish this without involving the Locations table, e.g.:
select b.* from
(bills b inner join accounts a on b.[account number] = a.[account number])
inner join
(select distinct c.[location num] from accounts c where c.[supplier only?] = true) l
on a.[location num] = l.[location num]
Alternatively, you can use a correlated subquery, e.g.:
select b.*
from bills b inner join accounts a on b.[account number] = a.[account number]
where exists
(select 1 from accounts c where c.[location num] = a.[location num] and c.[supplier only?] = true)

SQL - query to report MAX DATE results or NULL results

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)

Return results of ID NOT IN Certain Table

I am trying to write a write and the logic I am looking for is basically. If any User IDs from Table 1 do not exist in Table 2 show those in the results. So I did:
SELECT
UserColder.ContactName,
UserColder.Phone,
UserColder.Email,
UserColder.Website,
Country.Name,
UserColderZIP.[ZIP Code],
UserColderZIP.[State Abbreviation]
FROM
dbo.UserColder
LEFT OUTER JOIN dbo.CountryUser
ON UserColder.ID = CountryUser.[User ID]
INNER JOIN dbo.Country
ON CountryUser.[Foreign ID] = Country.ID
LEFT OUTER JOIN dbo.UserColderZIP
ON UserColder.ID = UserColderZIP.UserColder
WHERE
UserColder.ID NOT IN (CountryUser.[User ID])
It returns data without the Where But with the Where I am trying to get it to show all the results where the Id from UserColder don't have any records in CountryUser. Right now it is returning no results and I know ID 2 doesn't exist in there. Any idea what I am doing wrong?
Thanks!
Since you have a LEFT OUTER JOIN, to get records from UserColder that don't exist in CountryUser, check for NULL. The joined rows which have no corresponding value will return NULL.
WHERE
CountryUser.[User ID] IS NULL
Just grab the information from UserColder and make sure that the UserColder.ID is not in CountryUser by checking it (select statement in where).
SELECT
UserColder.ContactName,
UserColder.Phone,
UserColder.Email,
UserColder.Website,
Country.Name,
UserColderZIP.[ZIP Code],
UserColderZIP.[State Abbreviation]
FROM
dbo.UserColder
INNER JOIN dbo.Country
ON CountryUser.[Foreign ID] = Country.ID
LEFT OUTER JOIN dbo.UserColderZIP
ON UserColder.ID = UserColderZIP.UserColder
WHERE
UserColder.ID NOT EXISTS (select [user id] from dbo.CountryUser where CountryUser.[User ID])