Sql query: How to use right outer join correctly - sql

I have to tables: Register and Price.
CREATE TABLE [dbo].[Register](
[RegisterID] [int] NULL,
[GroupID] [int] NULL,
[TestID] [int] NULL)
CREATE TABLE [dbo].[Price](
[ID] [int] NULL,
[GroupID] [int] NULL,
[Price] [bigint] NULL,
[Status] [bit] NULL)
Assuming information similar to the image above, consider the following query
SELECT Price.*
FROM Register RIGHT OUTER JOIN Price ON Register.GroupID = Price.GroupID
WHERE (Price.Status = 1) AND (Register.TestID = 50)
The output will be displayed as shown below.
My expectation is that the first and second rows of the price table will be displayed. So where is my mistake and how should I change the query to get the right output?

The restriction on the Register table which currently appears in the WHERE clause would have to be moved to the ON clause to get the behavior you want:
SELECT p.*
FROM Register r
RIGHT JOIN Price p ON r.GroupID = p.GroupID AND r.TestID = 50;
WHERE p.Status = 1
Note that more typically you would express the above via a left join:
SELECT p.*
FROM Price p
LEFT JOIN Register r ON r.GroupID = p.GroupID AND r.TestID = 50
WHERE p.Status = 1;

Related

Problem with creating a SQL view with multiple joins

I am trying to create a view which will be a base for Excel export in API. Basically, what it contains is information about particular projects. To said projects calculations can be added (it all happens on the form on frontend). Those calculations are called EBIT, EBIT+ and OVI. User can add either one, two or all of them, so for example there will be projects with only EBIT, but also projects with only for example EBIT, but also only with EBIT+ and OVI. View needs to return the project information with all chosen calculations in one row, so because some type of calculations wont be chosen by user there needs to be a type safety as well.
Code of my view:
CREATE VIEW [Signoff].[ExecelReport_uvw]
AS SELECT
project.ProjectName,
project.CreatedOn,
project.ProjectId,
subCategory.SubCategoryName,
projectStatus.StatusName,
overallCategory.CategoryName,
projectUserResponsible.UserName,
valueImprovementType.ValueImprovementTypeName,
OVI.OverallImprovementTypeName,
project.NameOfSuplier,
improvementCalculation.Baseline,
improvementCalculation.ImpactValue,
project.ContractStartDate,
project.ContractEndDate,
userbusinessController.UserName as businessControllerName,
businessControllerStatus.ApprovalStatusName businessControllerStatus,
userbusinesOwner.UserName as businessOwnerName,
businesOwnerStatus.ApprovalStatusName as businessOwnerStatus,
userbusinessCFO.UserName as businessCFOName,
businessCFOStatus.ApprovalStatusName as businessCFOStatus,
project.IsEbitda,
improvementCalculation.EBITDA
FROM [Signoff].[Project] as project
LEFT JOIN [Signoff].[OverallImprovementType] as OVI on project.OverallImprovementTypeId = OVI.OverallImprovementTypeId
LEFT JOIN [Signoff].[SubCategory] as subCategory on project.GPSubCategory = subCategory.SubCategoryId
LEFT JOIN [Signoff].[Category] as overallCategory on project.GPCategory = overallCategory.CategoryId
LEFT JOIN [Signoff].[ValueImprovementType] as valueImprovementType on project.ValueImprovementTypeId = valueImprovementType.ValueImprovementTypeId
LEFT JOIN [Signoff].[Status] as projectStatus on project.ProjectStatus = projectStatus.StatusId
LEFT JOIN [Signoff].[User] as projectUserResponsible on project.ProjectResponsible = projectUserResponsible.UserId
LEFT JOIN [Signoff].[ProjectUser] as projectUserbusinessControler on project.ProjectId = projectUserbusinessControler.ProjectId AND projectUserbusinessControler.ProjectRoleId = 'A36FC6CD-9ED7-4AA8-B1BE-355E48BDE25A'
LEFT JOIN [Signoff].[User] as userbusinessController on projectUserbusinessControler.ApproverId = userbusinessController.UserId
LEFT JOIN [Signoff].[ApprovalStatus] as businessControllerStatus on projectUserbusinessControler.ApprovalStatusId = businessControllerStatus.ApprovalStatusId
LEFT JOIN [Signoff].[ProjectUser] as projectUserbusinessOwner on project.ProjectId = projectUserbusinessOwner.ProjectId AND projectUserbusinessOwner.ProjectRoleId = 'E1E23E4F-1CA4-4869-9387-43CEDAEBBBB0'
LEFT JOIN [Signoff].[User] as userbusinesOwner on projectUserbusinessOwner.ApproverId = userbusinesOwner.UserId
LEFT JOIN [Signoff].[ApprovalStatus] as businesOwnerStatus on projectUserbusinessOwner.ApprovalStatusId = businesOwnerStatus.ApprovalStatusId
LEFT JOIN [Signoff].[ProjectUser] as projectUserbusinessCFO on project.ProjectId = projectUserbusinessCFO.ProjectId AND projectUserbusinessCFO.ProjectRoleId = 'DA17CF66-1D61-460E-BF87-5D86744DF22A'
LEFT JOIN [Signoff].[User] as userbusinessCFO on projectUserbusinessCFO.ApproverId = userbusinessCFO.UserId
LEFT JOIN [Signoff].[ApprovalStatus] as businessCFOStatus on projectUserbusinessCFO.ApprovalStatusId = businessCFOStatus.ApprovalStatusId
LEFT JOIN [Signoff].[ProjectImprovementCalculation] as projectImprovementCalculation on project.ProjectId = projectImprovementCalculation.ProjectId
LEFT JOIN [Signoff].[ImprovementCalculation] as improvementCalculation on projectImprovementCalculation.ImprovementCalculationId = improvementCalculation.ImprovementCalculationId
Improvement calculation table:
CREATE TABLE [Signoff].[ImprovementCalculation]
(
[ImprovementCalculationId] INT NOT NULL IDENTITY,
[Baseline] INT NOT NULL,
[TotalSpend] INT NOT NULL,
[ImpactValue] INT NOT NULL,
[ImpactPercentage] INT NOT NULL,
[EBITDA] INT NOT NULL,
[CalculationType] VARCHAR (255) NOT NULL
)
GO
ALTER TABLE [Signoff].[ImprovementCalculation]
ADD CONSTRAINT [PK_ImprovemntCalculation] PRIMARY KEY([ImprovementCalculationId]);
GO
Project Improvement Calculation table:
CREATE TABLE [Signoff].[ProjectImprovementCalculation]
(
[ProjectImprovementCalculationId] INT NOT NULL IDENTITY,
[ProjectId] UNIQUEIDENTIFIER NOT NULL,
[ImprovementCalculationId] INT NOT NULL,
)
GO
ALTER TABLE [Signoff].[ProjectImprovementCalculation]
ADD CONSTRAINT [PK_ProjectImprovementCalculation] PRIMARY KEY([ProjectImprovementCalculationId]);
GO
ALTER TABLE [Signoff].[ProjectImprovementCalculation]
ADD CONSTRAINT FK_ProjectProjectImprovementCalculation
FOREIGN KEY (ProjectId) REFERENCES [Signoff].[Project](ProjectId);
GO
ALTER TABLE [Signoff].[ProjectImprovementCalculation]
ADD CONSTRAINT FK_ImprovementCalculationProjectImprovementCalculation
FOREIGN KEY (ImprovementCalculationId) REFERENCES [Signoff].[ImprovementCalculation](ImprovementCalculationId);
GO
Just in case, although I don't think it's needed, the project table:
CREATE TABLE [Signoff].[Project]
(
[ProjectId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWID()),
[ProjectName] NVARCHAR(50) NOT NULL,
[LegalEntity] UNIQUEIDENTIFIER NOT NULL,
[ValueImprovementTypeId] INT NOT NULL,
[OverallImprovementTypeId] INT NOT NULL,
[NameOfSuplier] NVARCHAR(50) NOT NULL,
[ContractStartDate] DATE NOT NULL,
[ContractEndDate] DATE NOT NULL,
[GPCategory] UNIQUEIDENTIFIER NOT NULL,
[GPSubCategory] UNIQUEIDENTIFIER NOT NULL,
[ProjectResponsible] UNIQUEIDENTIFIER NOT NULL,
[ProjectNumber] INT,
[FullProjectNumber] VARCHAR(55),
[ProjectStatus] UNIQUEIDENTIFIER NOT NULL DEFAULT '05c2f392-8b69-4915-a166-c4418889f9e8',
[IsCanceled] BIT NULL DEFAULT 0,
[IsEbitda] BIT NOT NULL DEFAULT 0,
[CreatedOn] DATETIME NOT NULL DEFAULT SYSDATETIME()
)
GO
ALTER TABLE [Signoff].[Project]
ADD CONSTRAINT [PK_Project] PRIMARY KEY([ProjectId]);
GO
ALTER TABLE [Signoff].[Project]
ADD CONSTRAINT [FK_ProjectStatus] FOREIGN KEY ([ProjectStatus]) REFERENCES [Signoff].[Status]([StatusId]);
GO
I have so came up with this solution, but it returns every single calculation in a different row in a table, and I want all calculations be in a single row with a project, so not what I am looking for:
LEFT JOIN [Signoff].[ProjectImprovementCalculation] as projectImprovementCalculation on project.ProjectId = projectImprovementCalculation.ProjectId
LEFT JOIN [Signoff].[ImprovementCalculation] as improvementCalculation on projectImprovementCalculation.ImprovementCalculationId = improvementCalculation.ImprovementCalculationId
Does anyone knows how to do it? Or I am approaching a problem from completely wrong way? If the information I have written is a bit chaotic, something isn't understandable, I can rephrase it.
I will assume that the available CalculationType values is fixed, that each project will have at most one improvement calculation per type, and that you wish to define fixed dedicated columns for BaseLine and ImpactValue each calculation type.
One approach is to use nested joins that will effectively LEFT JOIN the INNER JOINed combination of ProjectImprovementCalculation and ImprovementCalculation once for each calculation type. The results of each can then be referenced individually in the final select list.
Something like:
SELECT ...
IC_AAA.BaseLine, IC_AAA.ImpactValue,
IC_BBB.BaseLine, IC_BBB.ImpactValue,
...
FROM ...
LEFT JOIN Signoff.ProjectImprovementCalculation as PIC_AAA
JOIN Signoff.ImprovementCalculation as IC_AAA
ON IC_AAA.ImprovementCalculationId = PIC_AAA.ImprovementCalculationId
AND IC_AAA.CalculationType = 'AAA'
ON PIC_AAA.ProjectId = project.ProjectId
LEFT JOIN Signoff.ProjectImprovementCalculation as PIC_BBB
JOIN Signoff.ImprovementCalculation as IC_BBB
ON IC_BBB.ImprovementCalculationId = PIC_BBB.ImprovementCalculationId
AND IC_BBB.CalculationType = 'BBB'
ON PIC_BBB.ProjectId = project.ProjectId
...
The syntax is somewhat odd with two JOINs followed by two ON clauses. It would be clearer if parentheses were allowed, but (as far as I know) that is not part of the syntax.
There are several alternatives that accomplish the same. The following uses an OUTER APPLY:
SELECT ...
AAA.BaseLine, AAA.ImpactValue,
BBB.BaseLine, BBB.ImpactValue,
...
FROM ...
OUTER APPLY (
SELECT IC.*
FROM Signoff.ProjectImprovementCalculation as PIC
JOIN Signoff.ImprovementCalculation as IC
ON IC.ImprovementCalculationId = PIC.ImprovementCalculationId
AND IC.CalculationType = 'AAA'
WHERE PIC.ProjectId = project.ProjectId
) AAA
OUTER APPLY (
SELECT IC.*
FROM Signoff.ProjectImprovementCalculation as PIC
JOIN Signoff.ImprovementCalculation as IC
ON IC.ImprovementCalculationId = PIC.ImprovementCalculationId
AND IC.CalculationType = 'BBB'
WHERE PIC.ProjectId = project.ProjectId
) BBB
...
Using a Common Table Expression (CTE) can reduce some of the duplication as well as making the query a bit more readable.
;WITH ImprovementCTE AS (
SELECT PIC.ProjectId, IC.*
FROM Signoff.ProjectImprovementCalculation as PIC
JOIN Signoff.ImprovementCalculation as IC
ON IC.ImprovementCalculationId = PIC.ImprovementCalculationId
)
SELECT ...
AAA.BaseLine, AAA.ImpactValue,
BBB.BaseLine, BBB.ImpactValue,
...
FROM ...
LEFT JOIN ImprovementCTE AAA
ON AAA.ProjectId = project.ProjectId
AND AAA.CalculationType = 'AAA'
LEFT JOIN ImprovementCTE BBB
ON BBB.ProjectId = project.ProjectId
AND BBB.CalculationType = 'BBB'
...
You might also try using conditional aggregation in a single CROSS APPLY:
SELECT ...
IC.BaseLineAAA, IC.ImpactValueAAA,
IC.BaseLineBBB, IC.ImpactValueBBB,
...
FROM ...
CROSS APPLY (
SELECT
BaseLineAAA = SUM(CASE WHEN IC.CalculationType = 'AAA' THEN IC.BaseLine),
ImpactValueAAA = SUM(CASE WHEN IC.CalculationType = 'AAA' THEN IC.ImpactValue),
BaseLineBBB = SUM(CASE WHEN IC.CalculationType = 'BBB' THEN IC.BaseLine),
ImpactValueBBB = SUM(CASE WHEN IC.CalculationType = 'BBB' THEN IC.ImpactValue),
...
FROM Signoff.ProjectImprovementCalculation as PIC
JOIN Signoff.ImprovementCalculation as IC
ON IC.ImprovementCalculationId = PIC.ImprovementCalculationId
WHERE PIC.ProjectId = project.ProjectId
) IC
I expect there are additional approaches such as using a PIVOTs.
If the above appears to suit your needs, you should still run tests and examine the execution plans to see which performs best. Some may have a tendency to retrieve all ImprovementCalculation rows even when a subset of projects are selected.
To handle missing calculation types, you can use the ISNULL() function to provide a default. If you need to force a blank value in an otherwise numeric result, you might need to use something like ISNULL(CONVERT(VARCHAR(50), result), '').

Convert multi table sql join with derived table to Linq

I have a working SQL query as shown below. I want to create an equivalent Linq query. Not have much success.
select u.Id as userId, u.CustomerId, a.Id as appId, a.Created as appCreated
from EO_AppUsers u
left join EO_Policies p on (u.Id = p.AppUser_Id)
CROSS APPLY (
select TOP 1 id, created, AppUser_Id
from EO_Applications d
WHERE d.AppUser_id = u.id
order by Created desc ) a
CREATE TABLE [dbo].[EO_AppUsers] (
[Id] NVARCHAR (128) NOT NULL
CREATE TABLE [dbo].[EO_Policies] (
[Id] NVARCHAR (128) NOT NULL,
[AppUser_Id] NVARCHAR (128) NULL,
CREATE TABLE [dbo].[EO_Applications] (
[Id] NVARCHAR (128) NOT NULL,
[Created] DATETIME2 (7) NOT NULL,
[AppUser_Id] NVARCHAR (128) NULL,
EO_AppUsers has a EO_Policies and zero or more EO_Applications.
The SQL creates a report of all EO_AppUsers and associated EO_Policies and the latest/newest EO_Applications
The following handles returning EO_AppUsers and EO_Policies but is missing the EO_Applications. It needs the CROSS APPLY from the SQL query above.
var q = (from u in dbContext.EO_AppUsers
join p in dbContext.EO_Policies on u.Id equals p.AppUser.Id
//join a in dbContext.EO_Applications on u.Id equals a.AppUser.Id
select new
{
/// return fields from EO_AppUsers, EO_Policies and EO_Applications
});
You can try this linq query.
from u in EO_AppUsers
join p in EO_Policies on u.Id equals p.AppUser_Id into pl
from lp in pl.DefaultIfEmpty()
from a in EO_Applications.Where(d => d.AppUser_Id == u.Id)
.OrderByDescending(d => d.Created)
.Take(1)
.DefaultIfEmpty()
select new { userId = u.Id,
u.CustomerId,
appId = a.Id,
appCreated = a.Created,
polId = lp.Id
}

Left join does not return ull values

I have two tables:
AppWindowsEvent:
CREATE TABLE [AppWindowsEvent]
(
[idAppWindowEvent] INT IDENTITY(1,1)
, [idAppWindow] INT
, [idEventType] INT
, [Order] INT
, CONSTRAINT PK_idAppWindowEvent PRIMARY KEY ([idAppWindowEvent])
, CONSTRAINT FK_idAppWindowEvent_AppWindow FOREIGN KEY ([idAppWindow]) REFERENCES [AppWindow]([idAppWindow])
, CONSTRAINT FK_idAppWindowEvent_EventType FOREIGN KEY ([idEventType]) REFERENCES [EventType]([idEventType])
)
Event:
CREATE TABLE [Event]
(
[idEvent] [INT] IDENTITY(1,1) NOT NULL
, [idEventType] [INT] NOT NULL
, [idEntity] [INT] NOT NULL
, CONSTRAINT PK_IdEvent PRIMARY KEY([idEvent])
, CONSTRAINT [FK_Event_EventType] FOREIGN KEY([idEventType]) REFERENCES [EventType] ([idEventType])
)
When i run this query:
SELECT
*
FROM
AppWindowsEvent AWE
LEFT JOIN Event E ON AWE.idEventType = E.idEventType
WHERE
AWE.idMill = 1
AND AWE.idAppWindow = 1
ORDER BY
AWE.[Order] ASC
The result: not return nulls.
And when i run this
SELECT
*
FROM
AppWindowsEvent AWE
LEFT JOIN Event E ON AWE.idEventType = E.idEventType
AND E.[idEntity] = 1234
WHERE
AWE.idMill = 1
AND AWE.idAppWindow = 1
ORDER BY
AWE.[Order] ASC
Result: return nulls.
NOTE:
I need the entire set of data that are and are not already configured, in case you want a specific set of events, in the AND of ON can be filtered by specific idEntity of the Event table and the result returns well, but only for that idEntity, in my case I need all idEntity.
Try this
SELECT *
FROM
AppWindowsEvent AWE
LEFT JOIN Event E ON AWE.idEventType = E.idEventType
WHERE
AWE.idMill = 1
AND AWE.idAppWindow = 1
AND E.[idEntity] = 1234
ORDER BY
AWE.[Order] ASC
Or if you doesn't want appear null valor in second table, you can use Inner Join instead Left Join
SELECT *
FROM
AppWindowsEvent AWE
Inner JOIN Event E ON AWE.idEventType = E.idEventType
AND E.[idEntity] = 1234
WHERE
AWE.idMill = 1
AND AWE.idAppWindow = 1
ORDER BY
AWE.[Order] ASC

How to pass sub Select * to Join by fluent nhibernate

Can NH already create JOIN SELECT queries like
Select * From table1 Join ( Select * From table2 ...?
And why he can`t.
In my task I have a simple table:
TABLE [Message](
[Id] [int] NOT NULL,
[DocId] [int] NOT NULL,
[RecipientId] [bigint] NOT NULL,
[Text] [nvarchar](255) NULL,
[ArrivalDate] [date] NOT NULL
And Select:
SELECT msg.*
FROM [Message] msg
JOIN( SELECT msgMaxForDoc.DocId, MAX(msgMaxForDoc.ArrivalDate) AS ArrivalDate
FROM [Message] msgMaxForDoc
GROUP BY msgMaxForDoc.DocId) docAndDate ON msg.DocId = docAndDate.DocId AND msg.ArrivalDate = docAndDate.ArrivalDate
WHERE msg.RecipientId = #UserId
Full task sounds like: "Select all messages for user. If document (represented by DocId) contain more than one message, then get latest message". In result I must select one latest message for all DocId restricted by UserId
I found a similar question here:
nhibernate queryover join with subquery to get aggregate column
but it doesn't help (I don't have any links to another tablet DocId is just number).
And here Join a Subquery with NHibernate (but I can't separate select by two parts...)
As far as I know, it's impossible to join on a sub query using NHibernate. However, you could probably achieve your goal using the "WHERE EXISTS" method:
var dCriteria = DetachedCriteria.For<Message>("grouped")
.SetProjection(Projections.GroupProperty("grouped.DocId"))
.SetProjection(Projections.Max("grouped.ArrivalDate"))
.Add(Restrictions.EqProperty("msg.DocId", "grouped.DocId"))
.Add(Restrictions.EqProperty("msg.ArrivalDate", "grouped.ArrivalDate"))
.Add(Restrictions.Eq("grouped.RecipientId", #UserId));
var userMessages = Session.CreateCriteria<Message>("msg")
.Add(Subqueries.Exists(dCriteria)).List<Message>();

How to go about this implementation of query

My database table are as follow
CREATE TABLE [dbo].[vProduct](
[nId] [int] NOT NULL, //Primary key
[sName] [varchar](255) NULL
)
CREATE TABLE [dbo].[vProductLanguage](
[kProduct] [int] NOT NULL,
[kLanguage] [int] NOT NULL //Foriegn key to table vLanguage
)
CREATE TABLE [dbo].[vLanguage](
[nId] [int] NOT NULL, //Primary key
[sName] [varchar](50) NULL,
[language] [char](2) NULL
)
Table vProduct has relation to vProductLanguage on vProduct.nId = vProductLanguage.kLanguage
Table vProductLanguage has relation to vLanguage on vProductLanguage.kLanguage = vLanguage.nid
So its like table vProductLanguage will have languages which are being selected
Rows will be like image below
Table vProduct
Table vProductLanguage
Table vLanguage
What i want is select all Languages from table vLanguage and selected languages from table vProductLanguage. This will be associated with table vProduct.
I tried below query but it only returns me the languages which are associated with product.
select * from
vProductLanguage
left join vLanguage on vProductLanguage.kLanguage = vLanguage.nId
left join vProduct on vProductLanguage.kProduct = vProduct.nId
Where vProduct.nId = 1
I want to select all the rows from table vLanguage and table vProductLanguage.
Hope i made my question clear.
It sounds like you want to start your JOIN with the vLanguage table first:
select *
from vLanguage l
left join vProductLanguage pl
on l.nid = pl.kLanguage
left join vProduct p
on pl.kProduct = p.nid
and p.nid = 1
See SQL Fiddle with Demo.
This will return all rows from the vLanguage table and any matching rows from the vProductLanguage table. .
If you have more than one vProduct then you can rewrite the query slightly to:
select *
from vLanguage l
left join
(
select pl.kLanguage,
p.nid,
p.sName
from vProductLanguage pl
left join vProduct p
on pl.kProduct = p.nid
where p.nid = 1
) p
on l.nid = p.kLanguage
See SQL Fiddle with Demo