Prevent duplicates sql server - sql-server

Ok, in this query I'm extracting information from 5 tables, the table Company, Programmer, Tester, Manager and the table Contract. I will extract the Programmers', testers', and Managers' Names and Telephone Numbers, as well as the Company they work on, and this company is responsible for managing this program as a request by x person doesn't matter.
Problem is with the code below, a certain information will come out as many times as there is other information, like a programmer's Name and Tel Number will come out as many times as there are Managers and Testers on the company.
I tried with left outer join and it would give me even more results, so how can I fix this so next time a result won't be duplicated but say NULL?
SELECT DISTINCT pg.name,
pg.Tel_Nr,
Mgr.name,
Mgr.Tel_Nr,
Ts.Name,
Ts.Tel_Nr,
Pg.Name,
con.program_name
FROM Company AS Cm
INNER JOIN Programmer AS Pg ON Pg.company = Cm.name
INNER JOIN Manager AS Mg ON Mg.company = Cm.name
INNER JOIN Tester AS Ts ON Ts.company = Cm.name
INNER JOIN Contract AS Con ON Con.program_name = 'My Program'
AND Cm.name = Con.Company

Surely it would make more sense to produce a list of contact details with perhaps a job description. Something like this:
WITH Cte as (select Cm.name from
Contract as Con join Company as Cm on Cm.name = Con.Company
where Con.program_name = 'My Program')
SELECT pg.name, pg.Tel_Nr, 'Programmer' as JobTitle
FROM Cte INNER JOIN
Programmer as Pg on Pg.company = Cte.name
UNION ALL
SELECT Mgr.name, Mgr.Tel_Nr,'Manager' as JobTitle
FROM Cte INNER JOIN
Manager as Mg on Mg.company= Cte.name
UNION ALL
SELECT Ts.Name, Ts.Tel_Nr, 'Tester' as JobTitle
FROM Cte INNER JOIN
Tester as Ts on Ts.company = Cte.name
This solution deploys a Common Table Expression (labelled Cte) to avoid querying thr Company and Contract tables multiple times. Find out more.

Related

Joining multiple tables yields duplicates

In order to retrieve all Projects for a UserId, or all in case the user is admin, I want to join multiple tables. I'm using the statment in a TableAdapter query for MSSQL.
SELECT P.ID, P.CountryID, P.ProjectYear, P.Name, P.Objective, P.StartDate, P.EndDate, P.BaseCampaign, P.ManagerID, P.IsClosed, P.OrganisationUnitID, P.QualitativeZiele, P.QuantitativeZiele,
P.Herausforderungen, P.Learnings, P.ObjectiveQuantitativ, P.Remarks, P.ProjectOverallID, C.Name AS CountryName, O.Name AS OEName, R.RoleName
FROM wc_Projects AS P
INNER JOIN wc_OrganisationUnit AS O ON P.OrganisationUnitID = O.ID
INNER JOIN wc_Countries AS C ON P.CountryID = C.ID
INNER JOIN aspnet_Roles AS R ON C.ID = R.CountryID
INNER JOIN aspnet_UsersInRoles AS UR ON R.RoleId = UR.RoleId
WHERE (#ViewAll = 1) OR (UR.UserId = #UserId)
ORDER BY P.CountryID, P.OrganisationUnitID, P.ProjectYear DESC
In order to apply to the rather static approach for the table adapter, I start with the project.
Get all projects, resolve CountryName and OEName via FK's. Now look if you can find the role that is assoicated to the country. Then find the user that is attached to the role.
I know that this is a terrible query, but it's the only one somewhat applicable to the WebForms TableAdapter way to deal with it.
When I have a UserId that has one or multiple roles associated with countries it works. When a admin user, that has no roles with countries associated but ViewAll = 1 it breaks. I get constraint exceptions and the amount of results nearly tripple.
I tried rewriting the query, adding paranthesis and different joins. But none of it worked. How can I solve this?

TSQL - Return all record ids and the missing id's we need to populate

I have a job I need to automate to make sure a cache for some entities in my database is populated. I have the query below using CTE and CROSS JOIN but it doesn't run very quickly so I'm sure it can be improved.
The issue:
I have a database of employees
Each employee has a report of data compiled each month.
Each report has a set of 'components' and each of those components 'data' is pulled from an external source and cached in my database
The goal:
I want to set up a job to take a group of component Ids for 'this months report' and pre-cache the data if it doesn't exist.
I need to get a list of employees and the components they are missing in the cache for this months report. I will then set up a CRON job to process the queue.
The Question
My query below is slow - Is there a more efficient way to return a list of employees and the component ids that are missing in the cache?
The current SQL:
declare #reportDate datetime2 = '2019-10-01'; //the report publish date
declare #componentIds table (id int); // store the ids of each cachable component
insert #componentIds(id) values(1),(2),(3),(4),(5);
;WITH cteCounts
AS (SELECT r.Id as reportId, cs.componentId,
COUNT(1) AS ComponentCount
FROM EmployeeReports r
LEFT OUTER JOIN CacheStore cs on r.Id = cs.reportId and cs.componentId in (SELECT id FROM #componentIds)
GROUP BY r.Id, cs.componentId)
SELECT e.Id, e.name, _c.id as componentId, r.Id as reportId
FROM Employees e
INNER JOIN EmployeeReports r on e.Id = r.employeeId and r.reportDate = #reportDate
CROSS JOIN #componentIds _c
LEFT OUTER JOIN cteCounts AS cn
ON _c.Id = cn.componentId AND r.Id = cn.reportId
WHERE cn.ComponentCount is null
2 things I can suggest doing:
Use NOT EXISTS instead of a LEFT JOIN + IS NULL. The execution plan is prone to be different when you tell the engine that you want records that don't have any occurrence in a particular set Vs. joining and making sure that the joined column is null.
SELECT e.Id, e.name, _c.id as componentId, r.Id as reportId
FROM Employees e
INNER JOIN EmployeeReports r on e.Id = r.employeeId and r.reportDate = #reportDate
CROSS JOIN #componentIds _c
WHERE
NOT EXISTS (SELECT 'no record' FROM cteCounts AS cn
WHERE _c.Id = cn.componentId AND r.Id = cn.reportId)
Use temporary tables instead of CTE and/or variable tables. If you have to handle many rows, variable tables don't actually have statistics on and some complex CTE's might actually make lousy execution plans. Try using temporary tables instead of these 2 and see if the performance boosts. Also try creating relevant indexes on them if your row count is high.

Insert Into TABLE from 3 separate tables

I am getting my face kicked in....
I have a total of 4 tables
1. Business (BusinessID, CustomerID, BusName, Territory)
2. Customer (CustomerID, Name)
3. Sales (BusinessID, CustomerID, Territory, Jan, Feb, Mar, Apr, May, Jun)
4. Performance (this is the table I want the info in)
I've already created the table to have the following columns, BusinessID, CustomerID, BusName, Name, Territory, Jan,Feb,Mar,Apr,May,Jun
Every time I try to insert its not properly joining and I am getting a bunch of errors "multi-part identifier could not be bound"
insert into Performance (BusinessID, CustomerID, BusName, Name, Territory, January2018, February2018, March2018, April2018, May2018, June2018)
select Business.BusinessID, Customer.CustomerID, Business.BusName, Customer.Name, Sales.Territory, Sales.January2018, Sales.February2018, Sales.March2018, Sales.April2018, Sales.May2018, Sales.June2018
from Business A
inner join Customer B ON a.CustomerID = b.CustomerID
inner join Sales C ON b.CustomerID = c.CustomerID;
Due to this error I had to do 3 seperate insert into and that caused a bunch of nulls....
face palm is happening and could use some advice.
Image: enter image description here
Thanks,
VeryNew2SQL
You have used table ALIASES, so you have to use those aliases in you SELECT
A for Business, B for Customer and C for Sales.
Read about ALIASES here.
select A.BusinessID, B.CustomerID, A.BusName, B.Name, C.Territory, C.January2018, C.February2018, C.March2018, C.April2018, C.May2018, C.June2018
from Business A
inner join Customer B ON a.CustomerID = b.CustomerID
inner join Sales C ON b.CustomerID = c.CustomerID;
When you create a table alias in your FROM and JOIN clauses, you need to refer to the aliases in your SELECT statement and not the actual table names.
Alternatively, leave your SELECT statement as it is, and adjust your table names to remove the alias. You'll then need the join conditions to refer to your actual table names, rather than the alias. So for example;
select Business.BusinessID, Customer.CustomerID, Business.BusName, Customer.Name, Sales.Territory, Sales.January2018, Sales.February2018, Sales.March2018, Sales.April2018, Sales.May2018, Sales.June2018
from Business
inner join Customer ON Business.CustomerID = Customer.CustomerID
inner join Sales ON Customer.CustomerID = Sales.CustomerID;
Even just try running the SELECT statement above first to make sure you get the query correct before trying it in your insert.

Display only Companies that does not supply "Beverages" in Microsoft SQL

having trouble here with SQL statement for displaying companies that does not supply beverages. Now the problem here is that there are few companies that supply both beverage and other stuff. I need it to only display companies that does not supply beverages at all. When i run my modified SQL statement, i managed to get what i want but, for those companies that supply foods as well as beverages, it is still being displayed because of my WHERE clause i set to search for C.CategoryName != 'Beverages'. I just want to remove those companies that supply both beverages and foods so i can only display those companies that purely supply only foods.
Sorry if i have some grammer mistake or anything or my question is unclear. I just don't really know how to explain professionally as it is hard to describe my problem. Thanks for those who willing to help me, i am really appreciate it. And also i do not know if these info and source codes are sufficient regarding my problem. Will provide more info/source code if needed.
Pictures of Both Category & Products table.
Category Table
Products Table
Here's the Initial statement
SELECT P.ProductName
FROM Products AS P
INNER JOIN
Categories AS C
ON
C.CategoryID = P.CategoryID
AND C.CategoryName = 'Beverages'
Result
So far here's the modified statement
SELECT S.CompanyName, P.CategoryID, C.CategoryID FROM Suppliers AS S
INNER JOIN
Products AS P
ON S.SupplierID = P.SupplierID
LEFT OUTER JOIN
Categories AS C
ON
C.CategoryID = P.CategoryID
AND C.CategoryName != 'Beverages'
Result
Answer Suggested by Martin Smith.
With adding EXCEPT Clause to the SQL statement to my modified SQL statement, i managed to get it done. Here is the Answer to my problem.
SELECT S.CompanyName FROM Suppliers AS S
EXCEPT
SELECT S.CompanyName FROM Suppliers AS S
INNER JOIN
Products AS P
ON S.SupplierID = P.SupplierID
RIGHT JOIN
Categories AS C
ON
C.CategoryID = P.CategoryID
AND C.CategoryName = 'Beverages'
WITH FOODSUPPLIERS AS
(
SELECT DISTINCT SUPPLIERID
FROM PRODUCTS P
JOIN CATEGORY C ON C.CATEGORYID = P.CATEGORYID
WHERE CATEGORYNAME <> 'Beverages'
)
SELECT S.COMPANYNAME FROM SUPPLIERS S
JOIN FOODSUPPLIERS FS ON FS.SUPPLIERID = S.SUPPLIERID
The FOODSUPPLIERS CTE identifies suppliers in PRODUCTS that are not associated with any beverage products, i.e., associated only with food products. It's then a simple matter of joining the CTE to Suppliers to get the records for these suppliers.

SQL query returning not all data w.r.t to Territory Name

I'm trying to get Sales Person's data w.r.t Territory they work. I'm Using Adventureworks database tables Employee, Contact, SalesPerson, SalesTerritory.
Here is my query :
Select p.FirstName, p.LastName, h.EmployeeID, t.Name as "Territory Name"
from Person.Contact p
INNER JOIN HumanResources.Employee h ON p.ContactID = h.ContactID
INNER JOIN Sales.SalesPerson s ON s.SalesPersonID = h.EmployeeID
INNER JOIN Sales.SalesTerritory t ON s.TerritoryID = t.TerritoryID
Issue with this query is it results exactly one Territory. However there are sales persons who work in multiple territories... I need to result all of them.
Any help on this will be appreciated.
You must mean you want to know where Employees worked over time. As far as I can see in AdventureWorks2008 and AdventureWorks2008R2, Sales.SalesPerson is assigned a single TerritoryID, then in Sales.SalesTerritoryHistory there can be multiple TerritoryIDs per BusinessEntityID. For those DBs, use this:
select p.FirstName
,p.LastName
,e.LoginID -- there is no EmployeeID in this version of AdventureWorks
,st.Name as [TerritoryName]
from Sales.SalesPerson as sp
join Person.Person as p
on p.BusinessEntityID = sp.BusinessEntityID
join HumanResources.Employee as e
on e.BusinessEntityID = sp.BusinessEntityID
join Sales.SalesTerritoryHistory as sth
on sth.BusinessEntityID = sp.BusinessEntityID
join Sales.SalesTerritory as st
on sth.TerritoryID = st.TerritoryID
-- keep the where clause if you want to find only current TerritoryIDs
-- remove the where clause if you want to know all across time
where GETDATE() between sth.StartDate and isnull(sth.EndDate,'9999-1-1')
Clarify the version of AdventureWorks DB and I can correct the error (but don't say 2012, cause I ain't installing SQL Server 2012 :)

Resources