MSSQL: How to join only if its children not exist - sql-server

I want to left join but only if it does not have the specific child.
Here is my current query:
SELECT "chatRoom"."id" as id,
"chatRoom"."name" as name,
"chatRoom"."type" as type,
"chatRoom"."description" as description,
"chatRoom"."thumbnail" as thumbnail,
"chatRoom"."status" as status,
chats.[unreadCount] as unreadCount
FROM "chat_room" "chatRoom"
LEFT JOIN "chat_room_participant" "participants" ON "participants"."chatRoomId"="chatRoom"."id"
LEFT JOIN (
SELECT "chatRoom"."id" AS "chatRoomId", COUNT(readBy.id) AS "unreadCount"
FROM "chat" "chat"
LEFT JOIN "chat_room" "chatRoom" ON "chatRoom"."id"="chat"."chatRoomId"
LEFT JOIN "chat_read_by_chat_room_participant" "chat_readBy" ON "chat_readBy"."chatId"="chat"."id"
LEFT JOIN "chat_room_participant" "readBy" ON "readBy"."id"="chat_readBy"."chatRoomParticipantId"
WHERE NOT(readBy.UserId IN ('ca774a5f-a04d-ec11-ae58-74d83e04f9d3'))
OR "readBy"."id" IS NULL
GROUP BY "chatRoom"."id"
) "chats" ON chats.chatRoomId = "chatRoom"."id"
WHERE ('ALL' = 'ALL' OR "chatRoom"."status" = 'ALL')
AND "chatRoom"."applicationId" = '4ac752e9-004c-ec11-ae53-74d83e04f9d3'
AND "participants"."userId" = 'A97D66C4-014C-EC11-AE53-74D83E04F9D3'
ORDER BY "chatRoom"."lastUpdate" DESC
In this query, it returns the correct value only if the readBy is null or contains 1 entry which equals to the given userId.
So here I have the userId. I want to get all the unread chats count from each chatRoom that has the user as a participant.
The schema is like this:
chatRoom has many chats
chatRoom has many participants
chats has many to many `readBy` (participants)
So there is a column automatically created for the many-to-many relation:
column: chat_read_by_chat_room_participant
Contains:
|chatId|chatRoomParticipantId|
In my query above, the left join will get any readBy from another user:
WHERE NOT(readBy.UserId IN ('ca774a5f-a04d-ec11-ae58-74d83e04f9d3')) OR "readBy"."id" IS NULL GROUP BY "chatRoom"."id") "chats" ON chats.chatRoomId = "chatRoom"."id"
which I do not want. This will return the entry if the user already read the chat, but another users have read as well. I want to return the entry only if the user has not read the chat.
How can I do this?
CREATION QUERY
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[chat](
[id] [uniqueidentifier] NOT NULL,
[sentAt] [bigint] NOT NULL,
[type] [nvarchar](255) NOT NULL,
[status] [nvarchar](255) NOT NULL,
[message] [nvarchar](max) NOT NULL,
[filePath] [nvarchar](max) NULL,
[chatRoomId] [uniqueidentifier] NOT NULL,
[senderId] [nvarchar](255) NOT NULL,
[userId] [uniqueidentifier] NULL,
CONSTRAINT [PK_9d0b2ba74336710fd31154738a5] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[chat] ADD CONSTRAINT [DF_9d0b2ba74336710fd31154738a5] DEFAULT (newsequentialid()) FOR [id]
GO
ALTER TABLE [dbo].[chat] WITH CHECK ADD CONSTRAINT [FK_52af74c7484586ef4bdfd8e4dbb] FOREIGN KEY([userId])
REFERENCES [dbo].[chat_room_participant] ([id])
GO
ALTER TABLE [dbo].[chat] CHECK CONSTRAINT [FK_52af74c7484586ef4bdfd8e4dbb]
GO
ALTER TABLE [dbo].[chat] WITH CHECK ADD CONSTRAINT [FK_e49029a11d5d42ae8a5dd9919a2] FOREIGN KEY([chatRoomId])
REFERENCES [dbo].[chat_room] ([id])
GO
ALTER TABLE [dbo].[chat] CHECK CONSTRAINT [FK_e49029a11d5d42ae8a5dd9919a2]
GO
CREATE TABLE [dbo].[chat_read_by_chat_room_participant](
[chatId] [uniqueidentifier] NOT NULL,
[chatRoomParticipantId] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_f3dd24628d4644dd6e79bcd03d1] PRIMARY KEY CLUSTERED
(
[chatId] ASC,
[chatRoomParticipantId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[chat_read_by_chat_room_participant] WITH CHECK ADD CONSTRAINT [FK_011624ccd7e7b0281ef629f2930] FOREIGN KEY([chatId])
REFERENCES [dbo].[chat] ([id])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[chat_read_by_chat_room_participant] CHECK CONSTRAINT [FK_011624ccd7e7b0281ef629f2930]
GO
ALTER TABLE [dbo].[chat_read_by_chat_room_participant] WITH CHECK ADD CONSTRAINT [FK_2e33e3de9d7c91d426c09a24810] FOREIGN KEY([chatRoomParticipantId])
REFERENCES [dbo].[chat_room_participant] ([id])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[chat_read_by_chat_room_participant] CHECK CONSTRAINT [FK_2e33e3de9d7c91d426c09a24810]
GO
CREATE TABLE [dbo].[chat_room](
[id] [uniqueidentifier] NOT NULL,
[name] [nvarchar](255) NULL,
[type] [nvarchar](255) NOT NULL,
[thumbnail] [nvarchar](max) NULL,
[description] [nvarchar](max) NULL,
[status] [nvarchar](255) NOT NULL,
[applicationId] [uniqueidentifier] NOT NULL,
[lastUpdate] [bigint] NULL,
CONSTRAINT [PK_8aa3a52cf74c96469f0ef9fbe3e] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[chat_room] ADD CONSTRAINT [DF_8aa3a52cf74c96469f0ef9fbe3e] DEFAULT (newsequentialid()) FOR [id]
GO
ALTER TABLE [dbo].[chat_room] WITH CHECK ADD CONSTRAINT [FK_2226638e6b7665ec0259d246b2b] FOREIGN KEY([applicationId])
REFERENCES [dbo].[application] ([id])
GO
ALTER TABLE [dbo].[chat_room] CHECK CONSTRAINT [FK_2226638e6b7665ec0259d246b2b]
GO
CREATE TABLE [dbo].[chat_room_participant](
[id] [uniqueidentifier] NOT NULL,
[joinedAt] [bigint] NOT NULL,
[privilege] [nvarchar](255) NOT NULL,
[chatRoomId] [uniqueidentifier] NOT NULL,
[userId] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_15913cf37a762fce4c8d6a32a42] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY],
CONSTRAINT [UQ_ae9630d66f6c5d12afd1a991fec] UNIQUE NONCLUSTERED
(
[chatRoomId] ASC,
[userId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[chat_room_participant] ADD CONSTRAINT [DF_15913cf37a762fce4c8d6a32a42] DEFAULT (newsequentialid()) FOR [id]
GO
ALTER TABLE [dbo].[chat_room_participant] WITH CHECK ADD CONSTRAINT [FK_9f718459ea81b5130f81980ca08] FOREIGN KEY([userId])
REFERENCES [dbo].[user] ([id])
GO
ALTER TABLE [dbo].[chat_room_participant] CHECK CONSTRAINT [FK_9f718459ea81b5130f81980ca08]
GO
ALTER TABLE [dbo].[chat_room_participant] WITH CHECK ADD CONSTRAINT [FK_fb664f48a4ec615ec5cce90a25d] FOREIGN KEY([chatRoomId])
REFERENCES [dbo].[chat_room] ([id])
GO
ALTER TABLE [dbo].[chat_room_participant] CHECK CONSTRAINT [FK_fb664f48a4ec615ec5cce90a25d]
GO

It looks like you just need a WHERE NOT EXISTS correlated subquery.
Don't be tempted to just use LEFT JOIN IS NULL syntax, it is generally less efficient, as it hides from the optimizer that you are doing an anti-join. Occasionally it can be useful though.
Further notes:
Don't quote column and table names unless you have to. And generally avoid names that need quoting.
Don't alias columns that don't need aliasing.
Choose short meaningful table aliases.
Basic formatting and good use of whitespace helps readability
Your LEFT JOIN chat_room_participant logically becomes an INNER JOIN because of the WHERE.
You don't need to re-join chat_room in the grouped subquery, you can just group by the join column.
You may want to use a grouped OUTER APPLY instead of that subquery. It is unlikely to get a different query plan, but it can be easier to read.
COUNT(SomeNotNullValue) is the same as COUNT(*)
('ALL' = 'ALL' OR cr.status = 'ALL') only makes sense if cr.Status is nullable, otherwise you would just use cr.status = 'ALL'. Even if it is nullable, you may as well use (cr.Status IS NULL OR cr.status = 'ALL')
SELECT
cr.id,
cr.name,
cr.type,
cr.description,
cr.thumbnail,
cr.status,
c.unreadCount
FROM chat_room cr
JOIN chat_room_participant p ON p.chatRoomId = cr.id
LEFT JOIN (
SELECT
c.chatRoomId AS chatRoomId,
COUNT(*) AS unreadCount
FROM chat c
WHERE NOT EXISTS (SELECT 1
FROM chat_read_by_chat_room_participant chat_readBy
JOIN chat_room_participant readBy ON readBy.id = chat_readBy.chatRoomParticipantId
WHERE chat_readBy.chatId = c.id
AND readBy.UserId IN ('ca774a5f-a04d-ec11-ae58-74d83e04f9d3')
)
GROUP BY c.chatRoomId
) c ON c.chatRoomId = cr.id
WHERE (cr.Status IS NULL OR cr.status = 'ALL')
AND cr.applicationId = '4ac752e9-004c-ec11-ae53-74d83e04f9d3'
AND p.userId = 'A97D66C4-014C-EC11-AE53-74D83E04F9D3'
ORDER BY
cr.lastUpdate DESC;

It seems that what you want is an anti-semi join. That is, a join to demonstrate that a row does not exist.
The two typical methods are...
main_table AS m
LEFT JOIN
other_table AS o
ON o.m_id = m.id
AND o.user = 'xyz'
WHERE
o.id IS NULL
Or...
main_table AS m
WHERE
NOT EXISTS (
SELECT *
FROM other_table AS o
WHERE o.m_id = m.id
AND o.user = 'xyz'
)
Exactly how to apply this to your example is unclear as you have cluttered your question with too many other details. (It is not a Minimal Verifiable Example.)

Related

SQL Server foreign key data issue

I have 3 tables
crm.Documents
crm.GroupDocuments
schedular.Groups
Documents table looks like
Id Name
-----------
1 Test
GroupDocuments table looks like
Id | GroupId | DocumentId
---+---------+------------
1 | 1 | 1
2 | 2 | 1
Group table looks like
Id | Name
---+--------
1 | Sales
2 | Service
3 | Techs
The code to create these tables is :
CREATE TABLE [crm].[Documents]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NOT NULL,
[FileType] [int] NOT NULL,
[Bytes] [varbinary](max) NOT NULL,
[IsShared] [bit] NOT NULL,
[UploadDate] [datetime2](7) NOT NULL,
[LastSaveDate] [datetime2](7) NOT NULL,
CONSTRAINT [PK_Documents]
PRIMARY KEY CLUSTERED ([Id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
CREATE TABLE [scheduler].[Groups]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
CONSTRAINT [PK_Groups]
PRIMARY KEY CLUSTERED ([Id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [crm].[GroupDocuments]
(
[GroupId] [int] NOT NULL,
[DocumentId] [int] NOT NULL,
CONSTRAINT [PK_GroupDocuments]
PRIMARY KEY CLUSTERED ([GroupId] ASC, [DocumentId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [crm].[GroupDocuments] WITH CHECK
ADD CONSTRAINT [FK_GroupDocuments_Documents_GroupId]
FOREIGN KEY([GroupId]) REFERENCES [crm].[Documents] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [crm].[GroupDocuments] CHECK CONSTRAINT [FK_GroupDocuments_Documents_GroupId]
GO
ALTER TABLE [crm].[GroupDocuments] WITH CHECK
ADD CONSTRAINT [FK_GroupDocuments_Groups_DocumentId]
FOREIGN KEY([DocumentId]) REFERENCES [scheduler].[Groups] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [crm].[GroupDocuments] CHECK CONSTRAINT [FK_GroupDocuments_Groups_DocumentId]
GO
When I run
INSERT INTO crm.GroupDocuments (GroupId, DocumentId)
VALUES (3, 1)
I get this error:
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_GroupDocuments_Documents_GroupId".
The conflict occurred in database "Scheduler", table "crm.Documents",
column 'Id'.
There is an Id 3 on the group table and an Id 1 on the documents table. Can someone please explain why this error would occur?
You have inverted your FOREIGN KEY references....
ALTER TABLE [crm].[GroupDocuments]
ADD CONSTRAINT [FK_GroupDocuments_Documents_GroupId]
FOREIGN KEY([**GroupId**]) REFERENCES [crm].[**Documents**] ([Id])
You've criss-crossed the referenced columns in the definition of your foreign key constraints .... your DocumentID references Groups.Id, while GroupId references Documents.Id....
Change your FK statements to:
-- "GroupId" should reference "Groups.Id" (not "Documents.Id") .....
ALTER TABLE [crm].[GroupDocuments] WITH CHECK
ADD CONSTRAINT [FK_GroupDocuments_GroupId]
FOREIGN KEY([GroupId]) REFERENCES [scheduler].[Groups]([Id])
ON DELETE CASCADE
GO
-- and "DocumentId" should reference "Documents.Id" (not "Groups.Id")
ALTER TABLE [crm].[GroupDocuments] WITH CHECK
ADD CONSTRAINT [FK_GroupDocuments_DocumentId]
FOREIGN KEY([DocumentId]) REFERENCES [crm].[Documents]([Id])
ON DELETE CASCADE
GO

SQL JOIN not returning any results

I have a fairly simple join for three tables that is not returning any results. If I modify slightly I get sometime that appears jumbled (same data values in two fields). There is a Documents table from which I need the filename, a Variable table which has the variable definitions and names, and lastly I have a VariableValue table that the values for the variables are stored in. I am trying to get two values for each filename. Subject and Author. Here is the SQL call.
SELECT DISTINCT Documents.Filename, VariableValue.ValueText AS Author,
VariableValue.ValueText AS Subject
FROM Documents INNER JOIN
VariableValue ON Documents.DocumentID = VariableValue.DocumentID INNER JOIN
Variable AS VV1 ON VariableValue.VariableID = VV1.VariableID INNER JOIN
Variable AS VV2 ON VariableValue.VariableID = VV2.VariableID
WHERE (Documents.Filename LIKE N'ECO-%') AND (Documents.Deleted = 0) AND
(VV1.VariableName = N'Author') AND (VariableValue.ValueText <> '-') AND
(VV2.VariableName = N'Subject')
ORDER BY Author asc
If I remove the (VV2.VariableName = N'Subject') I get the following result:
Filename Author Subject
ECO-27533.docx billpark billpark
ECO-27630.docx billpark billpark
ECO-27683.docx billpark billpark
ECO-27790.docx billpark billpark
ECO-27812.docx billpark billpark
ECO-27975.docx billpark billpark
If I remove the (VV1.VariableName = N'Author') I get the following result:
Filename Author Subject
ECO-28720.docx 24006 LOW PWR BM DUMP 24006 LOW PWR BM DUMP
ECO-28595.docx 24052 PIN THREAD CORRECTION 24052 PIN THREAD CORRECTION
ECO-28517.docx 24087 24087
ECO-28791.docx 25584 REV A TO B 25584 REV A TO B
ECO-28714.docx 25873 PRESSURE RELEAF ASSY 25873 PRESSURE RELEAF ASSY
What I would like is:
Filename Author Subject
ECO-28720.docx billpark LOW PWR BM DUMP
Not sure what subtle thing I am missing?
TIA.
DDL
CREATE TABLE [dbo].[Variable](
[VariableID] [int] IDENTITY(1,1) NOT NULL,
[VariableName] [nvarchar](255) NOT NULL,
[VariableType] [int] NOT NULL,
[IsDeleted] [bit] NOT NULL,
[FlagUnique] [bit] NOT NULL,
[FlagMandatory] [bit] NOT NULL,
[FlagFreeUpdateAllVersion] [bit] NOT NULL CONSTRAINT [DF__Variable__FlagFr__05EEBAAE] DEFAULT ((0)),
[FlagFreeUpdateLatestVersion] [bit] NOT NULL CONSTRAINT [DF__Variable__FlagFr__06E2DEE7] DEFAULT ((0)),
CONSTRAINT [PK_Variable] PRIMARY KEY CLUSTERED
(
[VariableID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
AND
CREATE TABLE [dbo].[VariableValue](
[VariableID] [int] NOT NULL,
[DocumentID] [int] NOT NULL,
[ProjectID] [int] NOT NULL,
[RevisionNo] [int] NOT NULL,
[ConfigurationID] [int] NOT NULL,
[ValueText] [nvarchar](max) NOT NULL CONSTRAINT [DF_VariableValue_ValueText] DEFAULT (N''),
[ValueInt] [int] NULL,
[ValueFloat] [float] NULL,
[ValueDate] [datetime] NULL,
[ValueCache] [nvarchar](64) NOT NULL CONSTRAINT [DF_VariableValue_ValueCache] DEFAULT (''),
[IsLongText] [bit] NOT NULL CONSTRAINT [DF_VariableValue_IsLongText] DEFAULT ((0)),
CONSTRAINT [PK_VariableValue] PRIMARY KEY CLUSTERED
(
[VariableID] ASC,
[DocumentID] ASC,
[ProjectID] ASC,
[RevisionNo] ASC,
[ConfigurationID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[VariableValue] WITH NOCHECK ADD CONSTRAINT [FK_VariableValue_DocumentConfiguration] FOREIGN KEY([ConfigurationID])
REFERENCES [dbo].[DocumentConfiguration] ([ConfigurationID])
GO
ALTER TABLE [dbo].[VariableValue] CHECK CONSTRAINT [FK_VariableValue_DocumentConfiguration]
GO
ALTER TABLE [dbo].[VariableValue] WITH NOCHECK ADD CONSTRAINT [FK_VariableValue_Documents] FOREIGN KEY([DocumentID])
REFERENCES [dbo].[Documents] ([DocumentID])
GO
ALTER TABLE [dbo].[VariableValue] CHECK CONSTRAINT [FK_VariableValue_Documents]
GO
ALTER TABLE [dbo].[VariableValue] WITH CHECK ADD CONSTRAINT [FK_VariableValue_Projects] FOREIGN KEY([ProjectID])
REFERENCES [dbo].[Projects] ([ProjectID])
GO
ALTER TABLE [dbo].[VariableValue] CHECK CONSTRAINT [FK_VariableValue_Projects]
GO
ALTER TABLE [dbo].[VariableValue] WITH NOCHECK ADD CONSTRAINT [FK_VariableValue_Variable] FOREIGN KEY([VariableID])
REFERENCES [dbo].[Variable] ([VariableID])
GO
ALTER TABLE [dbo].[VariableValue] CHECK CONSTRAINT [FK_VariableValue_Variable]
GO
VariableValue.ValueText AS Author,
VariableValue.ValueText AS Subject
You're calling the exact same alias.columnname and assigning it to both Author and Subject.
We need to add projectID, revisionNo and ConfigurationID to the results as the PK for VariableValue is a composite key. If we don't include them, then the results could get mixed up for different projects/revsions or configurations.
So unless you have a rule that says only return the latest revision for a document... we may get multiple records back.
SELECT Documents.Filename
, VariableValue.ProjectID
, VariableValue.RevisionNo
, VariableValue.ConfigurationID
, max(Case when VV1.VariableName = N'Author' then VariableValue.ValueText END) as Author
, max(Case when VV1.VariableName = N'Subject' then VariableValue.ValueText END) AS Subject
FROM Documents
INNER JOIN VariableValue
ON Documents.DocumentID = VariableValue.DocumentID
INNER JOIN Variable AS VV1
ON VariableValue.VariableID = VV1.VariableID
WHERE (Documents.Filename LIKE N'ECO-%')
AND (Documents.Deleted = 0)
AND (VariableValue.ValueText <> '-')
AND (VV1.VariableName in (N'Author',N'Subject')
GROUP BY Documents.Filename
, VariableValue.ProjectID
, VariableValue.RevisionNo
, VariableValue.ConfigurationID
ORDER BY Author asc
With a terrific assist from xQbert this code does it.
SELECT DISTINCT Documents.Filename, max(Case when VV1.VariableName = N'Author' then VariableValue.ValueText END) as Author,
max(Case when VV1.VariableName = N'Subject' then VariableValue.ValueText END) AS Description
FROM Documents INNER JOIN
VariableValue ON Documents.DocumentID = VariableValue.DocumentID INNER JOIN
Variable AS VV1 ON VariableValue.VariableID = VV1.VariableID INNER JOIN
Variable AS VV2 ON VariableValue.VariableID = VV2.VariableID
WHERE (Documents.Filename LIKE N'ECO-%') AND (Documents.Deleted = 0) AND (VV1.VariableName in (N'Author',N'Subject') AND (VariableValue.ValueText <> '-'))
Group BY Documents.Filename
ORDER BY Author asc

UNIQUE constraint exception thrown on empty table INSERT [sql-server]

My INSERT statement fails while it is trying to add a new record into an empty table (Attribute) (no record yet).
I am surprised by the error raised by the system:
Violation of UNIQUE KEY constraint 'CK_Attribute_Name_IDproject'. Cannot insert duplicate key in object 'dbo.Attribute'. The duplicate key value is (dummy, 55).
The creation script for this table looks like
CREATE TABLE [dbo].[Attribute](
[ID] [int] IDENTITY(1,1) NOT NULL,
[IDproject] [int] NOT NULL,
[IDtype] [int] NOT NULL,
[IDgroup] [int] NOT NULL,
[name] [varchar](50) NOT NULL,
[color] [int] NULL,
[protected] [tinyint] NULL,
[datemodified] [datetime] NOT NULL,
PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [CK_Attribute_Name_IDproject] UNIQUE NONCLUSTERED
(
[name] ASC,
[IDproject] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
I skiped foreign keys references and default values which does not seem of interest in this context.
The UNIQUE constraint applies to [name] and [IDproject].
When running the following statement
SELECT *
FROM [dbo].[Attribute]
GO
SELECT *
FROM [dbo].[Project]
GO
I get the results
(0 row(s) affected)
(2 row(s) affected)
The first result indicats the Attribute Table is empty
The second that there are 2 Projects
then running the following INSERT in table Attribute it failed with the above mentioned UNIQUE CONSTRAINT error
INSERT INTO [dbo].[Attribute] ([IDproject], [name], [IDtype], [IDgroup], [color], [protected], [datemodified])
SELECT DISTINCT
p.[ID],'dummy',t.[ID],g.[ID],-1,0,getdate()
FROM [dbo].[Project] p
INNER JOIN [dbo].[Group] g ON g.[name]='none' AND g.[IDproject] = p.[ID]
INNER JOIN [dbo].[AttributeType] t ON t.[format]='text' AND g.[IDproject] = p.[ID]
WHERE p.[name]='TESTPROJ'
GO
How can i get such an error on an empty table ?
I have found the solution myself: the derived SELECT returns 2 records with 'dummy' due to a duplicate INTO one of table, AttributeType, with which INNER JOIN is performed.

An IF inside a check constraint SQL

I have this table..
CREATE TABLE [dbo].[Tipo_Servicios_Info](
[TSI_TS_Id] [int] NOT NULL,
[TS_Tar_Id] [int] NOT NULL,
[TS_PDI_Id] [int] NOT NULL,
[TSI_Descripcion] varchar(100),
[TSI_FechaIni] date not null,
[TSI_FechaFin] date not null,
[TSI_HoraMin] time,
[TSI_HoraMax] time,
[TSI_Duracion] varchar(50) not null,
[TSI_Unidad] varchar(50) not null,
[TSI_Cantidad] int not null,
CONSTRAINT [PK_TIPO_SERVICIOS_INFO] PRIMARY KEY CLUSTERED
(
[TSI_TS_Id] ASC,
[TS_Tar_Id] ASC,
[TS_PDI_Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
With some checks..
ALTER TABLE Dbo.Tipo_Servicios_Info
ADD CONSTRAINT Ck_Valores_Unidad CHECK(Tsi_Unidad IN('Dia', 'Hora'));
GO
ALTER TABLE Dbo.Tipo_Servicios_Info
ADD CONSTRAINT Df_Tipo_Servicios_Info_Tsi_Unidad DEFAULT 'Dia' FOR Tsi_Unidad;
GO
In this two above I set as default value of the TSI_Unidad to 'Dia', but this field only can contain Dia or Hora by the second constraint.
But i have a problem, I must define TSI_HoraMin and TSI_HoraMax that can be null(if TSI_Unidad is Dia, they have to be nullable), but if TSI_Unidad is Hora, [TSI_HoraMin] and [TSI_HoraMax] cant be null.
I want to create a new constraint but i dont know how can i do it..
This would be the idea..
ALTER TABLE [dbo].[Tipo_Servicios_Info]
ADD CONSTRAINT CK_HorasMin CHECK([TSI_HoraMin] is not null if [TSI_Unidad] = 'Hora')
GO
But this constraint, obviously, isnt well defined.
From what I gather you are trying to not allow a null value for TSI_HoraMin if TSI_Unidad = 'Hora`. In which case you could use:
ALTER TABLE [dbo].[Tipo_Servicios_Info]
ADD CONSTRAINT CK_HorasMin
CHECK(NOT([TSI_HoraMin] IS NULL AND [TSI_Unidad] = 'Hora'));

LINQ query issue: returning multiple records

I have 2 tables. Problem is I always get multiple records no matter what I do.
Pages
SubMenus
Relation is 1 to 1 (each submenu has a page)
SubMenus sample data:
submenu_id parentmenu_id display_name url_name
----------------------------------------------------
1 1 Home home
2 1 Contact contact
Pages table data:
page_id submenu_id page_title url_name
---------------------------------------------------------
1 1 Home Page home
2 1 Contact Page null
I want to retrieve single value from there JOIN where SubMenu.UrlName == Home
(from s in SubMenus
join p in Pages on s.Id equals p.SubMenuId
where s.UrlName == "home"
select new
{
s.Id, s.UrlName,
PageId = p.Id, p.Title, p.Html,
p.MetaAuthor, p.MetaKeywords, p.MetaDescription
}).FirstOrDefault();
But if I check the SQL and run it. I get single record without any problem. Below is what SQL generated
SELECT TOP (1)
[Extent1].[submenu_id] AS [submenu_id],
[Extent1].[url_name] AS [url_name],
[Extent2].[page_id] AS [page_id],
[Extent2].[page_title] AS [page_title],
[Extent2].[page_html] AS [page_html],
[Extent2].[meta_author] AS [meta_author],
[Extent2].[meta_keywords] AS [meta_keywords],
[Extent2].[meta_description] AS [meta_description]
FROM
[dbo].[SubMenu] AS [Extent1]
INNER JOIN
[dbo].[Pages] AS [Extent2] ON [Extent1].[submenu_id] = [Extent2].[submenu_id]
WHERE
N'home' = [Extent1].[url_name]
Table structure:
CREATE TABLE [dbo].[ParentMenu]
(
[parentmenu_id] [int] IDENTITY(1,1) NOT NULL,
[group_name] [nvarchar](50) NULL,
[title] [nvarchar](100) NULL,
[active] [bit] NOT NULL,
[index_order] [int] NULL,
CONSTRAINT [PK_ParentMenu]
PRIMARY KEY CLUSTERED ([parentmenu_id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[ParentMenu]
ADD CONSTRAINT [DF_ParentMenu_active] DEFAULT ((1)) FOR [active]
GO
ALTER TABLE [dbo].[ParentMenu]
ADD CONSTRAINT [DF_ParentMenu_index_order] DEFAULT ((0)) FOR [index_order]
GO
CREATE TABLE [dbo].[SubMenu]
(
[submenu_id] [int] IDENTITY(1,1) NOT NULL,
[parentmenu_id] [int] NOT NULL,
[display_name] [nvarchar](100) NULL,
[url_name] [nvarchar](100) NULL,
[index_order] [int] NULL,
[active] [bit] NOT NULL,
CONSTRAINT [PK_SubMenu]
PRIMARY KEY CLUSTERED ([submenu_id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [IX_SubMenu_1]
UNIQUE NONCLUSTERED ([url_name] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[SubMenu] WITH CHECK
ADD CONSTRAINT [FK_SubMenu_ParentMenu]
FOREIGN KEY([parentmenu_id]) REFERENCES [dbo].[ParentMenu] ([parentmenu_id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[SubMenu] CHECK CONSTRAINT [FK_SubMenu_ParentMenu]
GO
ALTER TABLE [dbo].[SubMenu]
ADD CONSTRAINT [DF_SubMenu_index_order] DEFAULT ((0)) FOR [index_order]
GO
ALTER TABLE [dbo].[SubMenu]
ADD CONSTRAINT [DF_SubMenu_active] DEFAULT ((1)) FOR [active]
GO
CREATE TABLE [dbo].[Pages]
(
[page_id] [int] IDENTITY(1,1) NOT NULL,
[submenu_id] [int] NOT NULL,
[page_title] [nvarchar](200) NULL,
[url_name] [nvarchar](100) NULL,
[page_html] [nvarchar](max) NULL,
[meta_author] [nvarchar](300) NULL,
[meta_keywords] [nvarchar](max) NULL,
[meta_description] [nvarchar](max) NULL,
[active] [bit] NOT NULL,
[creation_date] [date] NULL,
CONSTRAINT [PK_Pages]
PRIMARY KEY CLUSTERED ([page_id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Pages] WITH CHECK
ADD CONSTRAINT [FK_Pages_SubMenu]
FOREIGN KEY([submenu_id]) REFERENCES [dbo].[SubMenu] ([submenu_id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Pages] CHECK CONSTRAINT [FK_Pages_SubMenu]
GO
ALTER TABLE [dbo].[Pages]
ADD CONSTRAINT [DF_Pages_active] DEFAULT ((1)) FOR [active]
GO
You are saying that Problem is I always get multiple records no matter what I do.
The query that you show has top 1 in the very first line, it will always return just one row.
What's wrong with it?
From what I see your SQL query matches what your LINQ query does.
In this case LINQ query cannot possible return more rows than SQL returns.

Resources