Make one-to-one relation between two tables that have one-to-many relation in SQL - sql-server

I have these queries :
SELECT
dbo.Lines.Unit, dbo.Lines.LineNumber, dbo.Lines.DocumentNumber,
dbo.BaseMaterials.Name AS MaterialName,
dbo.MaterialDescriptions.Name AS MaterialDescription,
dbo.MaterialDescriptions.Description,
dbo.MaterialScopes.ScopeName, dbo.MaterialScopeObjectNames.ObjectName,
dbo.MaterialDescriptions.Size1, dbo.MaterialDescriptions.Size2,
dbo.MaterialDescriptions.ItemCode, dbo.Materials.Quantity,
dbo.Materials.Discipline, dbo.Materials.Id, dbo.Lines.Id AS LineId
FROM
dbo.Materials
INNER JOIN
dbo.Lines ON dbo.Materials.LineId = dbo.Lines.Id
INNER JOIN
dbo.BaseMaterials ON dbo.Lines.BaseMaterialId = dbo.BaseMaterials.Id
INNER JOIN
dbo.MaterialDescriptions ON dbo.Materials.MaterialDescriptionId = dbo.MaterialDescriptions.Id
INNER JOIN
dbo.MaterialScopes ON dbo.MaterialDescriptions.MaterialScopeId = dbo.MaterialScopes.Id
INNER JOIN
dbo.MaterialScopeObjectNames ON dbo.MaterialDescriptions.MaterialScopeObjectId = dbo.MaterialScopeObjectNames.Id
It returns 16000 records. I have another tables with joints name that has a relation with material table on lineId every material can have multi joints so my query when I add joints table is like this :
SELECT
dbo.Lines.Unit, dbo.Lines.LineNumber, dbo.Lines.DocumentNumber,
dbo.BaseMaterials.Name AS MaterialName,
dbo.MaterialDescriptions.Name AS MaterialDescription,
dbo.MaterialDescriptions.Description, dbo.MaterialScopes.ScopeName,
dbo.MaterialScopeObjectNames.ObjectName, dbo.MaterialDescriptions.Size1,
dbo.MaterialDescriptions.Size2, dbo.MaterialDescriptions.ItemCode,
dbo.Materials.Quantity, dbo.Materials.Discipline, dbo.Materials.Id,
dbo.Lines.Id AS LineId, dbo.Joints.TestPackageId
FROM
dbo.Materials
INNER JOIN
dbo.Lines ON dbo.Materials.LineId = dbo.Lines.Id
INNER JOIN
dbo.BaseMaterials ON dbo.Lines.BaseMaterialId = dbo.BaseMaterials.Id
INNER JOIN
dbo.MaterialDescriptions ON dbo.Materials.MaterialDescriptionId = dbo.MaterialDescriptions.Id
INNER JOIN
dbo.MaterialScopes ON dbo.MaterialDescriptions.MaterialScopeId = dbo.MaterialScopes.Id
INNER JOIN
dbo.MaterialScopeObjectNames ON dbo.MaterialDescriptions.MaterialScopeObjectId = dbo.MaterialScopeObjectNames.Id
INNER JOIN
dbo.Joints ON dbo.Materials.LineId = dbo.Joints.LineId
As you can see in the last line i make a join between joints and materials to access the testpackageid column in joints table .but my result returns 220000records .How can i change the join type i mean left or right to just returns 16000 records with its testpackageId

You can't using JOIN. However, you can using CROSS APPLY:
FROM . . .
dbo.MaterialScopeObjectNames
ON dbo.MaterialDescriptions.MaterialScopeObjectId = dbo.MaterialScopeObjectNames.Id CROSS APPLY
(SELECT TOP 1 j.*
FROM dbo.Joints j
WHERE dbo.Materials.LineId = j.LineId
) J
Normally, you would include an ORDER BY when using TOP so you have some control over the row being returned.
Also note that the use of table aliases makes the query easier to write and to read.

You can get it very easily with just a subquery. Leave your query as it is, and add a subquery to get that information from Joints.
SELECT dbo.Lines.Unit, dbo.Lines.LineNumber, dbo.Lines.DocumentNumber, dbo.BaseMaterials.Name AS MaterialName, dbo.MaterialDescriptions.Name AS MaterialDescription, dbo.MaterialDescriptions.Description,
dbo.MaterialScopes.ScopeName, dbo.MaterialScopeObjectNames.ObjectName, dbo.MaterialDescriptions.Size1, dbo.MaterialDescriptions.Size2, dbo.MaterialDescriptions.ItemCode, dbo.Materials.Quantity,
dbo.Materials.Discipline, dbo.Materials.Id, dbo.Lines.Id AS LineId,
(select top 1 dbo.Joints.TestPackageId from dbo.Joints where dbo.Joints.LineId = dbo.Materials.LineId)
FROM dbo.Materials INNER JOIN
dbo.Lines ON dbo.Materials.LineId = dbo.Lines.Id INNER JOIN
dbo.BaseMaterials ON dbo.Lines.BaseMaterialId = dbo.BaseMaterials.Id INNER JOIN
dbo.MaterialDescriptions ON dbo.Materials.MaterialDescriptionId = dbo.MaterialDescriptions.Id INNER JOIN
dbo.MaterialScopes ON dbo.MaterialDescriptions.MaterialScopeId = dbo.MaterialScopes.Id INNER JOIN
dbo.MaterialScopeObjectNames ON dbo.MaterialDescriptions.MaterialScopeObjectId = dbo.MaterialScopeObjectNames.Id

Related

SQL Server - using Over/Partition By in complex query

We have many complex queries which involve a lot of columns and joins (see the example below) that are implemented as views.
In some cases these queries return duplicate rows which then have to be programmatically removed by the consuming app. Therefore, we would like to enhance the SQL query to eliminate the duplicates, and speed up the retrieval process.
I know that I can use OVER / PARTITION BY logic to do this, but I am not sure of how to modify the queries to obtain a working syntax.
Here is an example:
SELECT
Main.MfgOrder.OrderNumber,
Main.MfgOrder.DesignBOMID,
Main.Design_Plant.PlantID,
Main.MfgOrder_Operation.OrderOpID,
Main.MfgOrder_Operation.DesignOpID,
Main.MfgOrder_Operation.OpSeq,
Main.MfgOrder_Operation.Description,
Main.MfgOrder_Operation.CompletionStatus,
Main.MfgOrder__Shift.OrderShiftID,
Main.MfgOrder__Shift.WorkCenterMachineID,
Main.MfgOrder___Event.OrderEventID,
Main.MfgOrder____Reel.OrderReelID,
Main.MfgOrder____Reel.ReelNumber,
Main.MfgOrder____Reel.Location,
Main.MfgOrder____Reel.Test_Status AS Test_Status_Reel,
Main.MfgOrder____Reel.Test_Disposition AS Test_Disposition_Reel,
Main.MfgOrder____Reel.LabReleased,
Main.MfgOrder____Reel.ShipReelsBypassSet,
Main.MfgOrder_____Length.OrderLengthID,
Main.MfgOrder_____Length.LengthType,
Main.MfgOrder_____Length.LocationOnReel,
Main.MfgOrder_____Length.LocationOnLength,
Main.MfgOrder_____Length.TrialNumber,
Main.MfgOrder_____Length.SampleNumber,
Main.MfgOrder_____Length.PrintNumber,
Main.MfgOrder_____Length.Test_Status AS Test_Status_Length,
Main.MfgOrder_____Length.Test_Category,
Main.MfgOrder_____Length.Test_Disposition AS Test_Disposition_Length,
Main.MfgOrder_____Length.SampleSubmittedBy,
Main.MfgOrder_____Length.SampleSubmittedDate,
Main.MfgOrder_____Length.BypassTesting,
Main.MfgOrder_____Length_OperatorQty.Sample1Destination,
Main.MfgOrder_____Length_OperatorQty.Sample2Destination,
Main.MfgOrder_____Length_OperatorQty.Sample3Destination,
Main.MfgOrder______Component.OrderComponentID,
Main.MfgOrder______Component.DesignComponentID,
Main.MfgOrder______Component.ItemNo,
Main.MfgOrder_______Test.LabTestID,
Main.MfgOrder_______Test.OrderTestID,
Main.MfgOrder_______Test.TestComplete,
Main.MfgOrder_______Test.TestStatus,
Main.MfgOrder________Marker2.OrderMarkerID,
Master.Color.ColorName,
Master.LabTest.ExcludeFromPassFail,
CASE
WHEN Main.Design_Component.Component_Label IS NULL
THEN 'Unknown'
ELSE Main.Design_Component.Component_Label
END AS Component_Label
FROM
Main.MfgOrder
INNER JOIN
Main.Design__BOM ON Main.MfgOrder.DesignBOMID = Main.Design__BOM.DesignBOMID
INNER JOIN
Main.Design_Plant ON Main.Design__BOM.DesignPlantID = Main.Design_Plant.DesignPlantID
INNER JOIN
Main.MfgOrder_Operation ON Main.MfgOrder.OrderNumber = Main.MfgOrder_Operation.OrderNumber
INNER JOIN
Main.MfgOrder__Shift ON Main.MfgOrder_Operation.OrderOpID = Main.MfgOrder__Shift.OrderOpID
INNER JOIN
Main.MfgOrder___Event ON Main.MfgOrder__Shift.OrderShiftID = Main.MfgOrder___Event.OrderShiftID
INNER JOIN
Main.MfgOrder____Reel ON Main.MfgOrder___Event.OrderEventID = Main.MfgOrder____Reel.OrderEventID
INNER JOIN
Main.MfgOrder_____Length ON Main.MfgOrder____Reel.OrderReelID = Main.MfgOrder_____Length.OrderReelID
LEFT OUTER JOIN
Main.MfgOrder______Component ON Main.MfgOrder_____Length.OrderLengthID = Main.MfgOrder______Component.OrderLengthID
LEFT OUTER JOIN
Main.MfgOrder_______Test ON Main.MfgOrder______Component.OrderComponentID = Main.MfgOrder_______Test.OrderComponentID
LEFT OUTER JOIN
Main.MfgOrder________Marker2 ON Main.MfgOrder_______Test.OrderTestID = Main.MfgOrder________Marker2.OrderTestID
LEFT OUTER JOIN
Main.Design_Component ON Main.MfgOrder______Component.DesignComponentID = Main.Design_Component.DesignComponentID
LEFT OUTER JOIN
Master.Color ON Main.MfgOrder______Component.TapeColorID = Master.Color.ColorNumber
LEFT OUTER JOIN
Master.LabTest ON Main.MfgOrder_______Test.LabTestID = Master.LabTest.LabTestID
LEFT OUTER JOIN
Main.MfgOrder_____Length_OperatorQty ON Main.MfgOrder______Component.OrderLengthID = Main.MfgOrder_____Length_OperatorQty.OrderLengthID
you can use row_number as below: Below query will not select duplicate only on OrderNumber, if you need to add other columns you add accordingly
Select * from (
Select
RowN = Row_Number() over( partition by Main.MfgOrder.OrderNumber order by Main.MfgOrder.OrderNumber),
--- All your select columns and all your query with joins
) a
Where a.RowN = 1
Is the entire row duplicated exactly? If so then just add DISTINCT
SELECT DISTINCT
...
FROM
...
If you are getting duplicate rows where most columns the same but some columns are different then GROUP BY the columns that are the same and select MIN(column_name) for the ones that are causing the extra rows to appear.

groupby in view of sql returns aggregate error

I am trying to create a view with this query as you can see here:
SELECT dbo.Lines.LineNumber, dbo.Lines.DocumentNumber, dbo.Joints.JointNumber, dbo.Joints.JointSize, dbo.Joints.ShopField, dbo.Joints.WPS, dbo.WeldDetails.StateStep2 AS WeldState, dbo.Welds.WeldNumber,
dbo.FitUps.FitUpNumber, MAX(dbo.WeldDetails.Id) AS WeldDetailId, MAX(dbo.FitUpDetails.Id) AS FitupDetailId, dbo.Joints.Id AS JointId, dbo.Ends.Name, dbo.Joints.THK, dbo.FitUpDetails.StateStep2 AS FitupState,
dbo.Joints.Revision, dbo.Joints.Note
FROM dbo.FitUps INNER JOIN
dbo.Welds INNER JOIN
dbo.Joints INNER JOIN
dbo.WeldDetails ON dbo.Joints.Id = dbo.WeldDetails.JointId INNER JOIN
dbo.FitUpDetails ON dbo.Joints.Id = dbo.FitUpDetails.JointId ON dbo.Welds.Id = dbo.WeldDetails.WeldId ON dbo.FitUps.Id = dbo.FitUpDetails.FitUpId INNER JOIN
dbo.Lines ON dbo.Joints.LineId = dbo.Lines.Id INNER JOIN
dbo.Ends ON dbo.Joints.EndId = dbo.Ends.Id
GROUP BY dbo.Joints.Id
But when i want to save the view i get this error :
Here is a part of my data :
Every joint id can have multi fitupdetailid and welddetailid in my view i want just show the maximum value of fitupdetailid and welddetailid of my joint.
I rewrote your query with a more readable join structure than what your GUI spit out. This should run for you and fix your error. Whether the results are what you want or not depends on your data. You may also want to re-order the grouping to group how you want, hierarchically. But all of those columns will need to be in the grouping in one form or another.
SELECT
dbo.Lines.LineNumber,
dbo.Lines.DocumentNumber,
dbo.Joints.JointNumber,
dbo.Joints.JointSize,
dbo.Joints.ShopField,
dbo.Joints.WPS,
dbo.WeldDetails.StateStep2 AS WeldState,
dbo.Welds.WeldNumber,
dbo.FitUps.FitUpNumber,
MAX(dbo.WeldDetails.Id) AS WeldDetailId,
MAX(dbo.FitUpDetails.Id) AS FitupDetailId,
dbo.Joints.Id AS JointId,
dbo.Ends.Name,
dbo.Joints.THK,
dbo.FitUpDetails.StateStep2 AS FitupState,
dbo.Joints.Revision,
dbo.Joints.Note
FROM
dbo.FitUps
INNER JOIN dbo.FitUpDetails ON dbo.FitUps.Id = dbo.FitUpDetails.FitUpId
INNER JOIN dbo.Joints ON dbo.Joints.Id = dbo.FitUpDetails.JointId
INNER JOIN dbo.WeldDetails ON dbo.Joints.Id = dbo.WeldDetails.JointId
INNER JOIN dbo.Welds ON dbo.Welds.Id = dbo.WeldDetails.WeldId
INNER JOIN dbo.Lines ON dbo.Joints.LineId = dbo.Lines.Id
INNER JOIN dbo.Ends ON dbo.Joints.EndId = dbo.Ends.Id
GROUP BY
dbo.Lines.LineNumber,
dbo.Lines.DocumentNumber,
dbo.Joints.JointNumber,
dbo.Joints.JointSize,
dbo.Joints.ShopField,
dbo.Joints.WPS,
dbo.WeldDetails.StateStep2,
dbo.Welds.WeldNumber,
dbo.FitUps.FitUpNumber,
dbo.Joints.Id,
dbo.Ends.Name,
dbo.Joints.THK,
dbo.FitUpDetails.StateStep2,
dbo.Joints.Revision,
dbo.Joints.Note

need max(id)for multiple rows but am receiving multiple max id's

I need to retrieve the latest loadid by location now with multiple joins.
for example ibiza's latest loadid is 692 and there are 2 records. The query below retrieves all of the loadid's for that location and not just the max. how can i achieve bringing back the latest records by location with the max(loadid) from the query below:
select
MAX(t.loadid) AS loadid,
a.process_id,
p.partitionkey,
-- t.partitionkey,
p.partdesc,
p.partname,
c.catname,
r.rule_name,
r.file_path,
a.execution_start_time,
a.execution_end_time,
a.records_processed
-- a.status,
-- a.last_updated_by as processed_by,
-- a.last_update_date
FROM
data t
INNER JOIN part p
ON t.partitionkey = p.partitionkey
INNER JOIN process a
ON t.loadid = a.process_id
INNER JOIN categ c
ON t.catkey = c.catkey
INNER JOIN balance r
ON t.RULE_ID = r.RULE_ID
WHERE
p.partname = 'ibiza'
GROUP BY
t.loadid,
a.process_id,
p.partitionkey,
t.partitionkey,
p.partdesc,
p.partname,
c.catname,
r.rule_name,
r.file_path,
a.execution_start_time,
a.execution_end_time,
a.records_processed;
You can use CTE :
;WITH MaxLoadId AS
(
select MAX(t.loadid) AS loadid, p.partitionkey, p.partdesc, p.partname
FROM data t
INNER JOIN part p ON t.partitionkey = p.partitionkey
WHERE p.partname = 'ibiza'
GROUP BY p.partitionkey, p.partdesc, p.partname
)
select
mli.loadid AS loadid,
a.process_id,
mli.partitionkey,
-- t.partitionkey,
mli.partdesc,
mli.partname,
c.catname,
r.rule_name,
r.file_path,
a.execution_start_time,
a.execution_end_time,
a.records_processed
-- a.status,
-- a.last_updated_by as processed_by,
-- a.last_update_date
FROM MaxLoadId mli
INNER JOIN data t ON mli.loadid = t.loadid
INNER JOIN process a ON mli.loadid = a.process_id
INNER JOIN categ c ON t.catkey = c.catkey
INNER JOIN balance r ON t.RULE_ID = r.RULE_ID

Use groupby with inner join to get unique record on single column

I need unique record on colum a.[id] so I am using group but it getting error
Msg 8120, Level 16, State 1, Line 3
Column 'dbo.assessment_dfn.name' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Query
SELECT a.[id] AS AssessmentID
--,ad.[code] AS Assessment_Dfn_Code
,ad.[name] AS Assessment_Dfn_name
,mdfn.[id] AS Module_Dfn_ID
,a.[student_stage]
,a.[assessments_sitting]
,a.[sitting_date]
,ads.[sitting_number]
,s.[id] AS StudentID
,a.[assessor] AS AssessorID
,mdfn.[lead] AS ModuleLead
,a.[submission_date]
,a.[status]
,a.[complete]
,a.[assessment]
,a.[saved]
,ele_cliPro.[grade] AS GradeID
,codGrd.[name] AS GradeName
FROM [adb_TestDb].[dbo].[assessments] AS a (NOLOCK)
INNER JOIN [adb_TestDb].[dbo].[student_stage] AS ss ON a.[student_stage] = ss.[id]
INNER JOIN [dbo].[assessment_dfn_sittings] ads WITH (NOLOCK) ON ads.[id] = a.[assessments_sitting]
INNER JOIN [adb_TestDb].[dbo].[students] AS s ON ss.student = s.[id]
INNER JOIN [dbo].[assessment_dfn] ad WITH (NOLOCK) ON ad.[id] = ads.[assessment]
INNER JOIN [dbo].[module_dfn] AS mdfn ON ad.[module] = mdfn.[id]
INNER JOIN [adb_TestDb].[dbo].[elements_clinicalprocedures] AS ele_cliPro ON a.[id] = ele_cliPro.[assessment]
INNER JOIN [adb_TestDb].[dbo].[codes_grade_details] AS codGrd ON ele_cliPro.[grade] = codGrd.[id] AND codGrd.[id] = 4
where a.id= 2532
group by a.[id]
As suggested by SQL you cannot get columns when using GROUP BY if they are not in that clause or in an aggregate function.
If you need only one record and you know all values are the same you could try to use an aggregate function for all the values required (eg MAX) but this solution should be used only if you are completely aware of getting only one among the possible values!

TSQL Inner select using outer join

I have a query that is working for the most part until I had to add the inner select for "Trainers".
As you can see in the code below, I am trying to get all of the trainers for each of the segment ID's.
I am getting an error on the first inner selects where clause WHERE trn.segmentID = tes.teSegmentID saying that tes.teSegmentID is not defined.
Is there another way to approach this query in order to get the trainers like I am trying to accomplish?
SELECT *,
(SELECT e2.[FirstName] AS trainerFirst,
e2.[LastName] AS trainerLast
FROM BS_Training_Trainers AS trn
LEFT OUTER JOIN
employeeTable AS e2
ON trn.trainerEmpID = e2.EmpID
WHERE trn.segmentID = tes.teSegmentID
FOR XML PATH ('trainer'), TYPE, ELEMENTS, ROOT ('trainers'))
FROM dbo.BS_TrainingEvents AS a
WHERE a.trainingEventID IN (SELECT tes.trainingEventID
FROM dbo.BS_TrainingEvent_Segments AS tes
INNER JOIN
dbo.BS_TrainingEvent_SegmentDetails AS tesd
ON tesd.segmentID = tes.teSegmentID
INNER JOIN
dbo.BS_LocaleCodes AS locale
ON locale.localeID = tesd.localeID
WHERE locale.location = 'Baltimore');
It seems like you're taking the scenic route towards this:
SELECT a.*,
X.[FirstName],
X.[LastName]
FROM dbo.BS_TrainingEvents AS a
LEFT OUTER JOIN (SELECT e2.[FirstName], e2.[LastName], locale.location FROM dbo.BS_TrainingEvent_Segments AS tes
INNER JOIN dbo.BS_Training_Trainers AS trn ON trn.segmentID = tes.teSegmentID
INNER JOIN dbo.BS_TrainingEvent_SegmentDetails AS tesd ON tesd.segmentID = tes.teSegmentID
INNER JOIN dbo.BS_LocaleCodes AS locale ON locale.localeID = tesd.localeID
LEFT OUTER JOIN employeeTable AS e2 ON trn.trainerEmpID = e2.EmpID) AS X ON a.trainingEventID = X.trainingEventID
WHERE X.location = 'Baltimore';
Not sure if I got all those joins right, it was hard to decode from all the nesting you have going on.
If I have guessed table relationships from their names correctly, the only way to solve this is to reference the same filtering condition twice: first, in the XML generation part, and second in the outer level of the query:
with cte as (
select distinct tes.trainingEventID, tes.teSegmentID
from dbo.BS_TrainingEvent_Segments AS tes
INNER JOIN dbo.BS_TrainingEvent_SegmentDetails AS tesd ON tesd.segmentID = tes.teSegmentID
INNER JOIN dbo.BS_LocaleCodes AS locale ON locale.localeID = tesd.localeID
WHERE locale.location = 'Baltimore'
)
SELECT a.*, (
SELECT e2.[FirstName] AS trainerFirst, e2.[LastName] AS trainerLast
FROM BS_Training_Trainers AS trn
LEFT OUTER JOIN employeeTable AS e2 ON trn.trainerEmpID = e2.EmpID
inner join cte c on trn.segmentID = c.teSegmentID
FOR XML PATH ('trainer'), TYPE, ELEMENTS, ROOT ('trainers')
)
FROM dbo.BS_TrainingEvents AS a
where exists (select 0 from cte c where c.testrainingEventID = a.trainingEventID);
It's difficult to tell whether this is completely correct, of course, but I hope you get the idea.
Oh yes, and if you would have an event with multiple Baltimore segments, you will never be able to tell which trainer takes which one. But you can always add more data into XML to resolve this.

Resources