Join table where table name is a value in another table - sql-server

I have been tasked with getting some data out of another database not designed by me, so the design cannot be altered.
Looking at the data I need to join a table dynamically based on the value held in another table.
E.G
SELECT * FROM TableA LEFT JOIN TableB oN TableA.TBID = TableB.ID LEFT JOIN TableB.TableOffset AS C ON C.TableAID = TableA.ID
So table B has a column called TableOffset, this holds the name of the table that needs to be joined on as C
(I've tried to do an SQLFIDDLE but the site isn't working with SQL Server ATM)
The issue is there are 112 attribute tables where the data could be, so doing a left join for every one of them would slow down the query I imagine.
So based on the below I would need to get a result set of:
| TableAID | TableATitle | AttrTableA21 | AttrTableA22 |
|----------|-------------|--------------|--------------|
|1 |test | Name | 2019 |
|2 |test2 | Name 2 | 2016 |
Example SQL Code
CREATE TABLE [dbo].[TableA](
[ID] [int] NOT NULL,
[Title] [nvarchar](100) NOT NULL,
[TableBID] [int] NOT NULL
);
CREATE TABLE [dbo].[TableB](
[ID] [int] NOT NULL,
[TableName] [nvarchar](100) NOT NULL
);
CREATE TABLE [dbo].[ATTR_A](
[ID] [int] NOT NULL,
[TableAID] [int] NOT NULL,
[A21] [nvarchar](100) NOT NULL,
[A22] [int] NOT NULL
);
CREATE TABLE [dbo].[ATTR_B](
[ID] [int] NOT NULL,
[TableAID] [int] NOT NULL,
[A21] [nvarchar](100) NOT NULL,
[A22] [int] NOT NULL
);
CREATE TABLE [dbo].[ATTR_C](
[ID] [int] NOT NULL,
[TableAID] [int] NOT NULL,
[A21] [nvarchar](100) NOT NULL,
[A22] [int] NOT NULL
);
CREATE TABLE [dbo].[ATTR_D](
[ID] [int] NOT NULL,
[TableAID] [int] NOT NULL,
[A21] [nvarchar](100) NOT NULL,
[A22] [int] NOT NULL
);
INSERT INTO TableA VALUES(1, 'test', 1);
INSERT INTO TableB VALUES(1, 'ATTR_C');
INSERT INTO ATTR_C VALUES (1, 1, 'Name', 2019);
INSERT INTO TableA VALUES(2, 'test2', 2);
INSERT INTO TableB VALUES (2, 'ATTR_A');
INSERT INTO ATTR_A VALUES (1, 2, 'Name 2', 2016);
```

You were told already, that this approach is the wrong one entirely. But if you have to stick with this, you might try this:
Your test scenario in a new database (carefull, if you use this database name already...)
USE master;
GO
CREATE DATABASE MyTestDb;
GO
USE MyTestDb;
GO
CREATE TABLE [dbo].[TableA](
[ID] [int] NOT NULL,
[Title] [nvarchar](100) NOT NULL,
[TableBID] [int] NOT NULL
);
CREATE TABLE [dbo].[TableB](
[ID] [int] NOT NULL,
[TableName] [nvarchar](100) NOT NULL
);
CREATE TABLE [dbo].[ATTR_A](
[ID] [int] NOT NULL,
[TableAID] [int] NOT NULL,
[A21] [nvarchar](100) NOT NULL,
[A22] [int] NOT NULL
);
CREATE TABLE [dbo].[ATTR_B](
[ID] [int] NOT NULL,
[TableAID] [int] NOT NULL,
[A21] [nvarchar](100) NOT NULL,
[A22] [int] NOT NULL
);
CREATE TABLE [dbo].[ATTR_C](
[ID] [int] NOT NULL,
[TableAID] [int] NOT NULL,
[A21] [nvarchar](100) NOT NULL,
[A22] [int] NOT NULL
);
CREATE TABLE [dbo].[ATTR_D](
[ID] [int] NOT NULL,
[TableAID] [int] NOT NULL,
[A21] [nvarchar](100) NOT NULL,
[A22] [int] NOT NULL
);
INSERT INTO TableA VALUES(1, 'test', 1);
INSERT INTO TableB VALUES(1, 'ATTR_C');
INSERT INTO ATTR_C VALUES (1, 1, 'Name', 2019);
INSERT INTO TableA VALUES(2, 'test2', 2);
INSERT INTO TableB VALUES (2, 'ATTR_A');
INSERT INTO ATTR_A VALUES (1, 2, 'Name 2', 2016);
GO
--I create an inlined table valued function where all attribute tables are returned as one set using UNION ALL
--The engine is smart enough, to avoid the call, if the parameter does not fit.
CREATE FUNCTION dbo.GetTheRightSet(#SetKey VARCHAR(10))
RETURNS TABLE
AS
RETURN
SELECT ID,TableAID,A21,A22 FROM dbo.ATTR_A WHERE #SetKey='ATTR_A'
UNION ALL
SELECT ID,TableAID,A21,A22 FROM dbo.ATTR_B WHERE #SetKey='ATTR_B'
UNION ALL
SELECT ID,TableAID,A21,A22 FROM dbo.ATTR_C WHERE #SetKey='ATTR_C'
UNION ALL
SELECT ID,TableAID,A21,A22 FROM dbo.ATTR_D WHERE #SetKey='ATTR_D'
GO
--This is how to use it
SELECT TableA.*
,TableB.*
,TheSet.*
FROM TableA
LEFT JOIN TableB ON TableA.TableBID = TableB.ID
OUTER APPLY dbo.GetTheRightSet(TableB.TableName) TheSet
GO
--Clean up (carefull with real data)
USE master;
GO
DROP DATABASE MyTestDb;

Related

Stored procedure query for selecting values

I have a Test table
CREATE TABLE [dbo].[Test](
[TestId] [int] IDENTITY(1,1) NOT NULL,
[TestName] [nvarchar](50) NOT NULL,
[UserId] [int] NOT NULL,
[isDelete] [bit] NOT NULL,
and Questions table as
CREATE TABLE [dbo].[Questions](
[Qid] [int] IDENTITY(1,1) NOT NULL,
[Tid] [int] NOT NULL,
[Qtype] [int] NOT NULL,
[Question] [nvarchar](max) NOT NULL,
[isDelete] [bit] NULL,
Questions table stores all the questions for each test with Tid as foreign key.
I want to write a stored procedure to fetch TestName, TestId and number of questions in each test in a single stored procedure. But I am unable to get this.
You can write the stored procedure as:
CREATE PROCEDURE [dbo].[procGetNumberofQuestionsForTest]
AS
BEGIN
SELECT T.[TestId], T.[TestName], COUNT(Q.[Qid]) AS NumberOfQuestions
FROM [dbo].[Test] T
JOIN [dbo].[Questions] Q ON Q.Tid = T.TestId
GROUP BY T.[TestId], T.[TestName]
END
If you want to get the result for specific Test, then pass the parameter as #TestId INT and add the WHERE clause as WHERE T.[TestId] = #TestId before the GROUP BY.
Try this (table create, insert, proc creation and execution included);
CREATE TABLE [dbo].[Test](
[TestId] [int] IDENTITY(1,1) NOT NULL,
[TestName] [nvarchar](50) NOT NULL,
[UserId] [int] NOT NULL,
[isDelete] [bit] NOT NULL)
go
CREATE TABLE [dbo].[Questions](
[Qid] [int] IDENTITY(1,1) NOT NULL,
[Tid] [int] NOT NULL,
[Qtype] [int] NOT NULL,
[Question] [nvarchar](max) NOT NULL,
[isDelete] [bit] NULL
)
go
insert into [dbo].[Test]
values('test #1',1,0)
go
insert into [dbo].[Questions]
values(1,1,'what is life',0)
go
create proc dbo.MyInfo
as
select
t.TestName,
t.TestId,
[No Questions]=COUNT(q.Qid)
from
[dbo].[Test] t
inner join
[dbo].[Questions] q on t.TestId=q.Qid
group by
t.TestName,
t.TestId
go
exec dbo.MyInfo
go

Read data from another columns to current table in SQL

I have these tables :
CREATE TABLE [dbo].[FileISOManagers]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[LineId] [int] NOT NULL,
[Revision] [nvarchar](max) NULL,
[FileName] [nvarchar](max) NULL,
[UserId] [int] NOT NULL,
[SubmitDateTime] [datetime] NOT NULL
)
CREATE TABLE [dbo].[Lines]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[LineNumber] [nvarchar](max) NULL,
[DocumentNumber] [nvarchar](max) NULL,
[Revision] [nvarchar](max) NULL
)
Every line has multiple revisions, so I just need to insert the last revision column value in FileISOManagers table inside Revision column in lines table automatically. I want do this in SQL Server. Is there any solution to do this? Should I use a trigger?
revision
-- Create trigger on table FileISOManagers for Insert statement
CREATE TRIGGER trgAfterInsert on FileISOManagers
FOR INSERT
AS declare #Revision varchar(100);
select #Revision=i.Revision from inserted i;
set #action='Inserted Record -- After Insert Trigger.'; insert into Lines(Revision)
values (#Revision);
PRINT 'AFTER INSERT trigger fired.
It can be done using joins and alias.
Example
INSERT INTO FileISOManagers (Revision ..columns)
SELECT *
FROM Lines ls
WHERE NOT EXISTS
(
SELECT *
FROM FileISOManagers
WHERE LineId = ls.id
) order by ls.id desc

sql join not showing some records

I'm trying to find all customers who have placed an order but there isn't a callback after their last order date.
My current query:
SELECT dbo.[Order].CustomerId, MAX(OrderDate) AS OrderDate, NextCallbackDate, UserName
FROM dbo.[Order]
LEFT OUTER JOIN (SELECT MAX(CallbackDate) NextCallbackDate, CustomerID
FROM AccountCallbacks
GROUP By CustomerId
) callbacks ON callbacks.CustomerID = dbo.[Order].CustomerID
LEFT OUTER JOIN dbo.aspnet_Users users ON dbo.[Order].UserID = users.UserId
WHERE (PaymentTypeID IN (2, 3, 4, 6, 8))
AND OrderDate >= NextCallbackDate
GROUP BY dbo.[Order].CustomerID, dbo.[Order].OrderDate,callbacks.NextCallbackDate, UserName
ORDER BY dbo.[Order].CustomerID
Tables:
AccountCallBacks:
[CallbackID] [int] IDENTITY(1,1) NOT NULL,
[UserID] [uniqueidentifier] NOT NULL,
[CustomerID] [int] NOT NULL,
[Created] [datetime] NOT NULL,
[CallbackDate] [date] NOT NULL,
[Enabled] [bit] NOT NULL,
[CallbackTimeID] [int] NULL,
[GaryCust] [bit] NULL,
[NotInterestedReasonID] [int] NULL
Order Table:
[OrderID] [int] IDENTITY(1,1) NOT NULL,
[CustomerID] [int] NULL,
[UserID] [uniqueidentifier] NULL,
[OrderDate] [datetime] NOT NULL,
[PaymentTypeID] [int] NULL,
[PaymentStatusID] [int] NOT NULL,
[PaymentDate] [datetime] NULL,
[TransactionRef] [varchar](50) NULL
And the aspnet_Users table is the usual .net membership users table
EDIT:
Apologies! I never actual said what was wrong! My query doesn't give me what I'm expecting one of the rows of data for a particular CustomerID isn't in the result set. And no, there should always be at least one AccountCallbacks.CallbackDate for every CustomerID as I'm joining on the dbo.[Order] table and they wouldn't be in there without first ever being in the AccountCallbacks table.
Feel free to ask any other info, help is greatly appreciated.

Can I make this trigger shorter / better / faster?

The task itself is very basic. I have a transaction table that moves a certain quantity of a product in or out of stock. It becomes a bit more difficult knowing that a product can be composed from multiple 'base' products and only the base products may exist in stock.
My current trigger works but works like this:
Create temporary table that can combine inserted, deleted and composed products.
Add inserted non composed products to this temp table
Add inserted composed products
Subtract deleted non composed products
Subtract deleted composed products
I now have a table containing all transactions of non composed products. But i.e. with an update of a row, the same product is added twice. Once subtracting the old value and once adding the new value.
Create second temporary table for the joined non composed products
Take a sum of the products and put these into the new temp table
Merge the stock with this temp table
Again.. my trigger works fine and i'm having no performance problem (yet) but I feel i'm missing something. I bet there is someone around here with a better solution then mine.
My tables and sample data: (fiddle here)
CREATE TABLE [dbo].[Product](
[id] [int] IDENTITY(1,1) NOT NULL,
[plantId] [int] NOT NULL,
[reference] [nvarchar](50) NOT NULL,
[composed] [bit] NOT NULL DEFAULT ((0)),
[name] [nvarchar](max) NOT NULL,
[unit] [nvarchar](50) NOT NULL,
[dateUpdate] [datetime] NOT NULL DEFAULT (getdate())
);
INSERT INTO [dbo].[Product] VALUES (2, 'FILLER VULPROF 2 BR', 0, 'FILLER VULPROF 2 BR', 'ton', GETDATE());
INSERT INTO [dbo].[Product] VALUES (2, 'K 0/2GEW BR', 0, 'K 0/2GEW BR', 'ton', GETDATE());
INSERT INTO [dbo].[Product] VALUES (2, 'K 14/20 BR', 0, 'K 14/20 BR', 'ton', GETDATE());
INSERT INTO [dbo].[Product] VALUES (2, 'KWS BR 3A31', 1, 'KWS BR 3A31', 'ton', GETDATE());
CREATE TABLE [dbo].[ProductComposition](
[id] [int] IDENTITY(1,1) NOT NULL,
[productId] [int] NOT NULL,
[parentId] [int] NOT NULL, -- This is the parent product
[quantity] [float] NOT NULL,
[dateUpdate] [datetime] NOT NULL DEFAULT (getdate())
);
INSERT INTO [dbo].[ProductComposition] VALUES (1, 4, 0.001, GETDATE());
INSERT INTO [dbo].[ProductComposition] VALUES (3, 4, 0.001, GETDATE());
CREATE TABLE [dbo].[Transaction](
[id] [int] IDENTITY(1,1) NOT NULL,
[load] [bit] NOT NULL,
[plantId] [int] NOT NULL,
[vehicleId] [int] NOT NULL,
[productId] [int] NOT NULL,
[contactId] [int] NOT NULL,
[quantity] [float] NOT NULL,
[dateUpdate] [datetime] NOT NULL DEFAULT (getdate()),
[isSynched] [bit] NOT NULL CONSTRAINT [DF_Transaction_isSynched] DEFAULT ((0))
);
CREATE TABLE [dbo].[Stock](
[id] [int] IDENTITY(1,1) NOT NULL,
[plantId] [int] NOT NULL,
[productId] [int] NOT NULL,
[quantity] [float] NOT NULL,
[dateUpdate] [datetime] NOT NULL DEFAULT (getdate())
);
My Trigger:
ALTER TRIGGER [dbo].[StockCalculate]
ON [dbo].[Transaction]
AFTER INSERT, UPDATE, DELETE AS
BEGIN
-- Create temporary table
CREATE TABLE #TransactionsComposed (
[load] [bit] NOT NULL,
[plantId] [int] NOT NULL,
[productId] [int] NOT NULL,
[quantity] [float] NOT NULL
);
-- Insert basic materials
INSERT INTO #TransactionsComposed (
[load], [plantId], [productId], [quantity])
SELECT
[Inserted].[load],
[Inserted].[plantId],
[Inserted].[productId],
[Inserted].[quantity]
FROM
[Inserted]
INNER JOIN [Product] ON [Product].[id] = [Inserted].[productId]
WHERE
[Product].[composed] = 0;
-- Insert operations
INSERT INTO #TransactionsComposed (
[load], [plantId], [productId], [quantity])
SELECT
[Inserted].[load],
[Inserted].[plantId],
[ProductComposition].[productId],
([Inserted].[quantity] * [ProductComposition].[quantity])
FROM
[Inserted]
INNER JOIN [Product] ON [Product].[id] = [Inserted].[productId]
INNER JOIN [ProductComposition] ON [ProductComposition].[parentId] = [Product].[id]
WHERE
[Product].[composed] = 1;
-- Insert basic materials but ALTER it's load status.
INSERT INTO #TransactionsComposed (
[load], [plantId], [productId], [quantity])
SELECT
CASE [Deleted].[load]
WHEN 0 THEN 1
WHEN 1 THEN 0
END,
[Deleted].[plantId],
[Deleted].[productId],
[Deleted].[quantity]
FROM
[Deleted]
INNER JOIN [Product] ON [Product].[id] = [Deleted].[productId]
WHERE
[Product].[composed] = 0;
-- Insert operations but ALTER it's load status.
INSERT INTO #TransactionsComposed (
[load], [plantId], [productId], [quantity])
SELECT
CASE [Deleted].[load]
WHEN 0 THEN 1
WHEN 1 THEN 0
END,
[Deleted].[plantId],
[ProductComposition].[productId],
([Deleted].[quantity] * [ProductComposition].[quantity])
FROM
[Deleted]
INNER JOIN [Product] ON [Product].[id] = [Deleted].[productId]
INNER JOIN [ProductComposition] ON [ProductComposition].[parentId] = [Product].[id]
WHERE
[Product].[composed] = 1;
-- Prepare multiple products for merge
CREATE TABLE #TransactionsJoined (
[plantId] [int] NOT NULL,
[productId] [int] NOT NULL,
[quantity] [float] NOT NULL
);
INSERT INTO #TransactionsJoined
([plantId], [productId], [quantity])
SELECT
[plantId],
[productId],
SUM(CASE [load]
WHEN 0 THEN [quantity]
WHEN 1 THEN 0 - [quantity]
END)
FROM
#TransactionsComposed
GROUP BY [plantId], [productId]
-- Merge composed transactions into the stock
MERGE [Stock]
USING #TransactionsJoined
ON [Stock].[plantId]=#TransactionsJoined.[plantId]
AND [Stock].[productId]=#TransactionsJoined.[productId]
WHEN NOT MATCHED BY TARGET THEN
INSERT ([plantId], [productId], [quantity])
VALUES (
#TransactionsJoined.[plantId],
#TransactionsJoined.[productId],
#TransactionsJoined.[quantity])
WHEN MATCHED THEN
UPDATE SET
[Stock].[quantity] = [Stock].[quantity] + #TransactionsJoined.[quantity],
[Stock].[dateUpdate] = GETDATE();
END

SQl results duplication

I have 3 tables
CREATE TABLE [Distributors]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
)
INSERT INTO [Distributors]([Name]) VALUES ('A')
INSERT INTO [Distributors]([Name]) VALUES ('B')
CREATE TABLE [Points]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[Distributor_ID] [int] NULL,
[Point_Count] [int] NULL,
[Point_Type] [nvarchar](max) NULL,
[Point_Level] [int] NULL,
[From_Distributor] [int] NULL,
[Is_Calculated] [bit] NULL,
[Point_Date] [datetime]
)
INSERT INTO [Points] ([Distributor_ID], [Point_Count], [Point_Type],[Point_Level], [From_Distributor], [Is_Calculated], [Point_Date])
VALUES(1, 1, 'AN', '', 2, 'True', '2015-02-16')
INSERT INTO [Points] ([Distributor_ID], [Point_Count], [Point_Type],[Point_Level], [From_Distributor], [Is_Calculated], [Point_Date])
VALUES(1, 1, 'Offer', '', 2, 'True', '2015-02-16')`
CREATE TABLE [dbo].[Profit]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[Distributor_ID] [int] NULL,
[Profit] [decimal](18, 2) NULL,
[Percentage] [int] NULL,
[Profit_Date] [datetime] NULL,
[From_Distributor_ID] [int] NULL,
[Is_Paid] [bit] NULL,
[Paid_By] [int] NULL
)
INSERT INTO [Profit]([Distributor_ID], [Profit], [Percentage],[Profit_Date], [From_Distributor_ID], [Is_Paid], [Paid_By])
VALUES (1, '80', 10, '2015-02-16', 2, 'False', 0)
INSERT INTO [Profit]([Distributor_ID], [Profit], [Percentage],[Profit_Date], [From_Distributor_ID], [Is_Paid], [Paid_By])
VALUES (1, '40', 5, '2015-02-16', 2, 'False', 0)
When I do the select query with join statement I face a duplication
SELECT distinct Points.[ID]
,Points.[Distributor_ID]
,[Point_Count]
,[Point_Type]
,[Point_Level]
,[From_Distributor]
,[Is_Calculated]
,[Point_Date]
--,Profit.Profit
FROM
[Points]
INNER JOIN
Profit ON Points.From_Distributor = Profit.From_Distributor_ID
AND Points.Distributor_ID = Profit.Distributor_ID
WHERE
(Points.[Distributor_ID] = 1)
AND ([Point_Date] >= '2/16/2015')
AND ([Point_Date] <= '2/28/2015')
results are tow rows
when using " Profit.Profit " in the select statement it got 4 rows
i think problem in join statement
please i need your advice

Resources