Profit calculation from last 2 year to year 2016 - sql

Respected Techie,
May any one please help me with this critical scenario.
I am trying to compare Sales value of year 2016 with sales value of (2015,2014)
and the difference stored in alias column as Sales_growth for year 2016 ,
for year (2015,2014) the alias column be as '1'
Based on the key column month, sales_manager_code and sales_group.
but when there is no key match (month, sales_manager_code ,sales_group.)
then the alias column sales_growth 1
Formula for calculation
SGR = (sales(2015 or 2014)-sales (2016)) / Sales_growth(2015)
Table Structure and Data
DECLARE #T TABLE
(
YEAR VARCHAR (50),
MONTH VARCHAR (50),
SALES_MANAGER_CODE VARCHAR (50),
SALES_GROUP VARCHAR (50),
NetProductSales VARCHAR (50)
)
INSERT #T
SELECT '2015', '10', '10', 'ARS', '126431.16' UNION ALL
SELECT '2015', '10', '4', '4', '1247439.2' UNION ALL
SELECT '2014', '11', '4', '2', '1399367.53' UNION ALL
SELECT '2016', '10', '10', 'ARS', '126431.16' UNION ALL
SELECT '2016', '10', '4', '4', '1247439.2' UNION ALL
SELECT '2016', '11', '4', '2', '1399367.53' UNION ALL
SELECT '2015', '8', '11', '0', '44518.18'
Expected Output Here sales_margin is alias column to store result
/*
YEAR MONTH SALES_MANAGER_CODE SALES_GROUP NetProductSales SALES_MARGIN
2015, 10, 10, ARS,126431.16, 1
2015, 10, 4, 4, 1247439.2, 1
2014, 11, 4, 2, 1399367.53, 1
2016, 10, 10, ARS, 3565898.25, -27.20426744
2016, 10, 4, 4, 5469856.25, 3.384868016
2016, 11, 4, 2, 45268912.65, 31.34955198
2015, 8, 11, 0, 44518.18, 1
*/
Thanks,

I'm not sure where you get SalesGrowth but using a LEFT OUTER join will give you a NULL where there is no corresponding join row for which you can then use COALESCE to convert the NULL to 1.
DECLARE #T TABLE
(
YEAR VARCHAR (50),
MONTH VARCHAR (50),
SALES_MANAGER_CODE VARCHAR (50),
SALES_GROUP VARCHAR (50),
NetProductSales decimal( 28, 16 )
)
INSERT #T
SELECT '2015', '10', '10', 'ARS', 126431.16 UNION ALL
SELECT '2015', '10', '4', '4', 1247439.2 UNION ALL
SELECT '2014', '11', '4', '2', 1399367.53 UNION ALL
SELECT '2016', '10', '10', 'ARS', 126431.16 UNION ALL
SELECT '2016', '10', '4', '4', 1247439.2 UNION ALL
SELECT '2016', '11', '4', '2', 1399367.53 UNION ALL
SELECT '2015', '8', '11', '0', 44518.18
select *
, COALESCE( ( tprv.NetProductSales - t16.NetProductSales ) / ???? ), 1 ) as SALES_MARGIN
from #T t16
left outer join #T tprv on tprv.YEAR = t16.YEAR - 1
and tprv.MONTH = t16.MONTH
and tprv.SALES_MANAGER_CODE = t16.SALES_MANAGER_CODE
and tprv.SALES_GROUP = t16.SALES_GROUP
where t16.YEAR = 2016

Related

Combine subqueries without views

I work with languages where I can assign intermediate outputs to a variable and then work the with variables to create a final output. I know SQL doesn't work this way as much. Currently I have queries that require me to make subsets of tables and then I want to join those subsets together. I can mimic the variable assignment I do in my native languages using a VIEW but I want to know how to do this using a single query (otherwise the database will get messy with views quickly).
Below is a MWE to make 2 initial tables DeleteMe1 and DeleteMe2 (at the end). Then I'd use these two views to get current snapshots of each table. Last I'd use LEFT JOIN with the views to merge the 2 data sets.
Is there a way to see the code SQL uses on the Join Snapshoted Views header code I supply below
How could I eliminate the views intermediate step and combine into a single SQL query?
Create views for current snapshot:
CREATE VIEW [dbo].[CurrentSnapshotDeleteMe1]
AS
SELECT DISTINCT *
FROM
(SELECT
t.[Id]
,t.[OppId]
,t.[LastModifiedDate]
,t.[Stage]
FROM
[dbo].DeleteMe1 as t
INNER JOIN
(SELECT
[OppId], MAX([LastModifiedDate]) AS MaxLastModifiedDate
FROM
[dbo].DeleteMe1
WHERE
LastModifiedDate <= GETDATE()
GROUP BY
[OppId]) AS referenceGroup ON t.[OppId] = referenceGroup.[OppId]
AND t.[LastModifiedDate] = referenceGroup.[MaxLastModifiedDate]) as BigGroup
GO
CREATE VIEW [dbo].[CurrentSnapshotDeleteMe2]
AS
SELECT DISTINCT *
FROM
(SELECT
t.[Id]
,t.[OppId]
,t.[LastModifiedDate]
,t.[State]
FROM
[dbo].DeleteMe2 AS t
INNER JOIN (
SELECT [OppId], MAX([LastModifiedDate]) AS MaxLastModifiedDate
FROM [dbo].DeleteMe2
WHERE LastModifiedDate <= GETDATE()
GROUP BY [OppId]
) as referenceGroup
ON t.[OppId] = referenceGroup.[OppId] AND t.[LastModifiedDate] = referenceGroup.[MaxLastModifiedDate]
) as BigGroup
GO
Join snapshoted views:
SELECT
dm1.[Id] as IdDM1
,dm1.[OppId]
,dm1.[LastModifiedDate] as LastModifiedDateDM1
,dm1.[Stage]
,dm2.[Id] as IdDM2
,dm2.[LastModifiedDate] as LastModifiedDateDM2
,dm2.[State]
FROM [dbo].[CurrentSnapshotDeleteMe1] as dm1
LEFT JOIN [dbo].[CurrentSnapshotDeleteMe2] as dm2 ON dm1.OppId = dm2.OppId
Create original tables:
CREATE TABLE DeleteMe1
(
[Id] INT,
[OppId] INT,
[LastModifiedDate] DATE,
[Stage] VARCHAR(250),
)
INSERT INTO DeleteMe1
VALUES ('1', '1', '2019-04-01', 'A'),
('2', '1', '2019-05-01', 'E'),
('3', '1', '2019-06-01', 'B'),
('4', '2', '2019-07-01', 'A'),
('5', '2', '2019-08-01', 'B'),
('6', '3', '2019-09-01', 'C'),
('7', '4', '2019-10-01', 'B'),
('8', '4', '2019-11-01', 'C')
CREATE TABLE DeleteMe2
(
[Id] INT,
[OppId] INT,
[LastModifiedDate] DATE,
[State] VARCHAR(250),
)
INSERT INTO DeleteMe2
VALUES (' 1', '1', '2018-07-01', 'California'),
(' 2', '1', '2017-11-01', 'Delaware'),
(' 3', '4', '2017-12-01', 'California'),
(' 4', '2', '2018-01-01', 'Alaska'),
(' 5', '4', '2018-02-01', 'Delaware'),
(' 6', '2', '2018-09-01', 'Delaware'),
(' 7', '3', '2018-04-01', 'Alaska'),
(' 8', '1', '2018-05-01', 'Hawaii'),
(' 9', '4', '2018-06-01', 'California'),
('10', '1', '2018-07-01', 'Connecticut'),
('11', '2', '2018-08-01', 'Delaware'),
('12', '2', '2018-09-01', 'California')
I work with languages where I can assign intermediate outputs to a variable and then work the with variables to create a final output. I know SQL doesn't work this way as much.
Well, that's not true, sql does work this way, or at least sql-server does. You have temp tables and table variables.
Although you named your tables DeleteMe, from your statements it seems like it's the views you wish to treat as variables. So I'll focus on this.
Here's how to do it for your first view. It puts the results into a temporary table called #tempData1:
-- Optional: In case you re-run before you close your connection
if object_id('tempdb..#snapshot') is not null
drop table #snapshot1;
select
distinct t.Id, t.OppId, t.LastModifiedDate, t.Stage
into #snapshot1
from dbo.DeleteMe1 as t
inner join (
select OppId, max(LastModifiedDate) AS MaxLastModifiedDate
from dbo.DeleteMe1
where LastModifiedDate <= getdate()
group by OppId
) referenceGroup
on t.OppId = referenceGroup.OppId
and t.LastModifiedDate = referenceGroup.MaxLastModifiedDate;
The hashtag tells sql server that the table is to be stored temporarially. #tempTable1 will not survive when your connection closes.
Alternatively, you can create a table variable.
declare #snapshot1 table (
id int,
oppId int,
lastModifiedDate date,
stage varchar(50)
);
insert #snapshot1 (id, oppId, lastModifiedDate, stage)
select distinct ...
This table is discarded as soon as the query has finished executing.
From there, you can join on your temp tables:
SELECT dm1.[Id] as IdDM1, dm1.[OppId],
dm1.[LastModifiedDate] as LastModifiedDateDM1, dm1.[Stage],
dm2.[Id] as IdDM2, dm2.[LastModifiedDate] as LastModifiedDateDM2,
dm2.[State]
FROM #snapshot1 dm1
LEFT JOIN #snapshot2 dm2 ON dm1.OppId = dm2.OppId
Or your table variables:
From there, you can join on your temp tables:
SELECT dm1.[Id] as IdDM1, dm1.[OppId],
dm1.[LastModifiedDate] as LastModifiedDateDM1, dm1.[Stage],
dm2.[Id] as IdDM2, dm2.[LastModifiedDate] as LastModifiedDateDM2,
dm2.[State]
FROM #snapshot1 dm1
LEFT JOIN #snapshot2 dm2 ON dm1.OppId = dm2.OppId

SQL Server : creating a top 3 view - having difficulties

This is my first time asking a SQL related question. I am having a difficult time getting this query to work.
Basically, I have a database that has 3 tables Course, Faculty and Adjunct. This query is supposed to create a view named Top3Enrollment that returns FirstName, LastName, TotalStudents, and MaxEnrollment of the 3 faculty members with the largest total enrollment for their courses, along with the highest enrollment among the classes they teach.
When attempting to write the query I get an error with selecting the column FirstName
My query:
CREATE VIEW Top3Enrollment
AS
SELECT TOP 3 PERCENT
FirstName, LastName, SUM(Enrollment), MAX(Enrollment)
FROM
Faculty
JOIN
Course ON Faculty.Faculty_ID = Course.Faculty_ID
ORDER BY
MAX(Enrollment);
The error I get is:
Msg 8120, Level 16, State 1, Procedure Top3Enrollment, Line 3 [Batch Start Line 0]
Column 'Faculty.FirstName' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
My database structures:
CREATE TABLE Faculty
(
Faculty_ID VARCHAR(2),
LastName VARCHAR(20),
FirstName VARCHAR(20),
Department VARCHAR(10),
Campus VARCHAR(10)
);
INSERT INTO Faculty
VALUES ('1', 'Brown', 'Joe', 'Business', 'Kent'),
('2', 'Smith', 'John', 'Economics', 'Kent'),
('3', 'Jones', 'Sally', 'English', 'South'),
('4', 'Black', 'Bill', 'Economics', 'Deerwood'),
('5', 'Green', 'Gene', 'Business', 'South');
CREATE TABLE Course
(
Course_ID CHAR(2),
Ref_Number CHAR(5),
Faculty_ID VARCHAR(2),
Term Char(1),
BegDate SMALLDATETIME,
Enrollment INTEGER,
TotRev FLOAT
);
INSERT INTO Course
VALUES ('1', '12345', 'a', 'A', '2016-01-08 00:00:00', 24, 12345.32 ),
('2', '54321', '3', 'B', '2016-02-04 00:00:00', 18, 21435.51 ),
('3', '13524', '1', 'B', '2016-02-04 00:00:00', 7, 1256.67 ),
('4', '24653', '1', 'C', '2016-03-04 00:00:00', 29, 54421.11 ),
('5', '98765', '5', 'A', '2016-01-08 00:00:00', 35, 246753.23),
('6', '14862', '2', 'B', '2016-02-04 00:00:00', 14, 9876.87),
('7', '96032', '1', 'C', '2016-03-04 00:00:00', 8, 863159.31),
('8', '81256', '5', 'A', '2016-01-08 00:00:00', 5, 98762.14),
('9', '64321', '2', 'C', '2016-03-04 00:00:00', 23, 2965.98),
('10','90908', 'a', 'A', '2016-01-08 00:00:00', 45, 91724.02),
('11','90908', '3', 'A', '2016-01-08 00:00:00', 23, 73725.77),
('12','90908', '3', 'A', '2016-01-08 00:00:00', 16, 84224.29),
('13','90908', 'b', 'A', '2016-01-08 00:00:00', 13, 42719.82);
CREATE TABLE Adjuncts
(
Faculty_ID Char(2),
LastName VARCHAR(20),
FirstName VARCHAR(20),
Department VARCHAR(10),
Campus VARCHAR(10)
);
INSERT INTO Adjuncts
VALUES ('a', 'Rogers', 'Aaron', 'Business', 'Kent'),
('b', 'Manning', 'Peyton', 'Economics', 'North'),
('c', 'Drew', 'Maurice', 'English', 'Cecil'),
('d', 'Griffin', 'Robert', 'Music', 'Deerwood'),
('e', 'Goodell', 'Roger', 'Economics', 'South'),
('f', 'Vilma', 'Jonathan', 'Business', 'Kent');
Note:
I Understand I cannot have Order By but I wouldn't know what else to use
Added the database code
When you use aggregate functions like sum and max together with other columns you need to group your aggregations by those other columns.
Add GROUP BY like this
SELECT TOP 3 PERCENT FirstName, LastName, SUM(Enrollment), MAX(Enrollment)
FROM Faculty
JOIN Course ON Faculty.Faculty_ID = Course.Faculty_ID
GROUP BY FirstName, LastName
ORDER BY MAX(Enrollment);

How to join to tables whose names are stored as values in another table?

Ι have some tables ( eg. [Table1], [Table2], [Table3] and so on ) with a [ID] as primary key and a RecTime as DATETIME on each.
Αlso Ι have a table [Files] that hold files in a varbinary(max) column, and refers to the other tables having their Names and IDs.
[Table2], [Table3] and others have different structure, but share the [ID] and [RecTime] column exactly as in [Table1]
Below is a quick sample to visualize data.
DECLARE #Table1 as table (
[ID] [bigint]
, [RecTime] [datetime]
)
DECLARE #Table2 as table (
[ID] [bigint]
, [RecTime] [datetime]
)
DECLARE #Table3 as table (
[ID] [bigint]
, [RecTime] [datetime]
)
DECLARE #Files as table (
[ID] [bigint]
, [tblName] nvarchar(255) NULL
, [tblID] bigint NULL
, [BinaryData] varbinary(max)
/* and some other columns */
)
INSERT INTO #Table1 (
[ID]
, [RecTime]
)
SELECT '1', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '2', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '3', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '4', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '5', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
INSERT INTO #Table2 (
[ID]
, [RecTime]
)
SELECT '11', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '12', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '13', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '14', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '15', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
INSERT INTO #Table3 (
[ID]
, [RecTime]
)
SELECT '21', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '22', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '23', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '24', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '25', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
INSERT INTO #Files (
[ID]
, [tblName]
, [tblID]
, [BinaryData]
)
SELECT '1', 'Table1', '1', 0x010203040506
UNION ALL SELECT '2', 'Table1', '2', 0x010203040506
UNION ALL SELECT '3', 'Table1', '2', 0x010203040506
UNION ALL SELECT '4', 'Table1', '3', 0x010203040506
UNION ALL SELECT '5', 'Table1', '4', 0x010203040506
UNION ALL SELECT '6', 'Table1', '5', 0x010203040506
UNION ALL SELECT '7', 'Table1', '5', 0x010203040506
UNION ALL SELECT '8', 'Table2', '11', 0x010203040506
UNION ALL SELECT '9', 'Table2', '11', 0x010203040506
UNION ALL SELECT '10', 'Table2', '12', 0x010203040506
UNION ALL SELECT '11', 'Table2', '13', 0x010203040506
UNION ALL SELECT '12', 'Table2', '14', 0x010203040506
UNION ALL SELECT '13', 'Table2', '12', 0x010203040506
UNION ALL SELECT '14', 'Table2', '15', 0x010203040506
UNION ALL SELECT '15', 'Table3', '21', 0x010203040506
UNION ALL SELECT '16', 'Table3', '22', 0x010203040506
UNION ALL SELECT '17', 'Table3', '24', 0x010203040506
UNION ALL SELECT '18', 'Table3', '23', 0x010203040506
UNION ALL SELECT '19', 'Table3', '25', 0x010203040506
UNION ALL SELECT '20', 'Table3', '25', 0x010203040506
UNION ALL SELECT '21', 'Table3', '21', 0x010203040506
SELECT * FROM #Table1
SELECT * FROM #Table2
SELECT * FROM #Table3
SELECT * FROM #Files
How can I join [Files] table to other tables, the Name and ID of which derive from a value in '[Files]' table ?
I need [BinaryData] from [Files] table and [RecTime] from respective table reference in [Files] table.
The real problem is that [Table1], [Table2] and [Table3] are not the only tables that are referred [Files] table. New tables can be created, for which binary data must be stored in [Files] table.
So I'm looking for a way to "join" them dynamically.
P.S. I'm not the creator of this system, and can not perform any structural change on it, but just trying to solve this problem.
Any help would be appreciated.
One approach is to create a cte that will contain all the tables data (of course, using dynamic sql to create it), and then select from files left join that cte.
This way, the dynamic sql is quite simple to write and maintain, and also the sql it statement it produces is very simple:
DECLARE #SQL varchar(max) = ''
SELECT #SQL = #SQL +' UNION ALL SELECT ID,
RecTime,
'''+ tblName +''' AS TableName
FROM ' + tblName
FROM (
SELECT DISTINCT tblName FROM files
) x
-- replace the first 'UNION ALL' with ';WITH allTables as ('
SELECT #SQL = STUFF(#SQL, 1, 11, ';WITH allTables as (')
+')
SELECT *
FROM Files
LEFT JOIN allTables ON(tblName = TableName AND tblId = allTables.Id)'
The sql statemet you get from this is:
;WITH allTables as (
SELECT ID, RecTime, 'Table1' AS TableName
FROM Table1
UNION ALL
SELECT ID, RecTime, 'Table2' AS TableName
FROM Table2
UNION ALL
SELECT ID, RecTime, 'Table3' AS TableName
FROM Table3
)
SELECT *
FROM Files
LEFT JOIN allTables ON(tblName = TableName AND tblId = allTables.Id)
To execute it:
EXEC(#SQL)
Results:
ID tblName tblID BinaryData ID RecTime TableName
1 Table1 1 123456 1 31.03.2060 00:00:00 Table1
2 Table1 2 123456 2 03.12.1997 00:00:00 Table1
3 Table1 2 123456 2 03.12.1997 00:00:00 Table1
4 Table1 3 123456 3 02.07.2039 00:00:00 Table1
5 Table1 4 123456 4 17.06.1973 00:00:00 Table1
6 Table1 5 123456 5 06.12.2076 00:00:00 Table1
7 Table1 5 123456 5 06.12.2076 00:00:00 Table1
8 Table2 1 123456 NULL NULL NULL
9 Table2 3 123456 NULL NULL NULL
10 Table2 3 123456 NULL NULL NULL
11 Table2 4 123456 NULL NULL NULL
12 Table2 5 123456 NULL NULL NULL
13 Table2 5 123456 NULL NULL NULL
14 Table2 5 123456 NULL NULL NULL
15 Table3 1 123456 NULL NULL NULL
16 Table3 1 123456 NULL NULL NULL
17 Table3 1 123456 NULL NULL NULL
18 Table3 3 123456 NULL NULL NULL
19 Table3 3 123456 NULL NULL NULL
20 Table3 3 123456 NULL NULL NULL
21 Table3 4 123456 NULL NULL NULL
Live demo on rextester
One solution is to use a cursor that executes some dynamic SQL for each row in the #Files table:
-- Copy table variables into temporary tables so they can be referenced from dynamic SQL
SELECT * INTO #Table1 FROM #Table1;
SELECT * INTO #Table2 FROM #Table2;
SELECT * INTO #Table3 FROM #Table3;
-- Create a temporary table for storing the results
CREATE TABLE #results (
[ID] [bigint]
, [tblName] nvarchar(255) NULL
, [tblID] bigint NULL
, [BinaryData] varbinary(max)
, [RecTime] [datetime]
);
-- Declare placeholders and cursor
DECLARE #ID bigint;
DECLARE #tblName nvarchar(255);
DECLARE #tblID bigint;
DECLARE #BinaryData varbinary(max);
DECLARE #RecTime datetime;
DECLARE #sql nvarchar(max);
DECLARE #params nvarchar(max);
DECLARE files_cursor CURSOR FOR
SELECT ID, tblName, tblID, BinaryData
FROM #Files
-- Loop over all rows in the #Files table
OPEN files_cursor
FETCH NEXT FROM files_cursor INTO #ID, #tblName, #tblID, #BinaryData
WHILE ##FETCH_STATUS = 0
BEGIN
-- Find the referenced table row and extract its RecTime.
SET #RecTime = NULL;
SET #sql = CONCAT(
'SELECT #RecTime = RecTime FROM #', #tblName, ' WHERE ID = ', #tblID);
SET #params = '#RecTime datetime out';
EXEC SP_EXECUTESQL #sql, #params, #RecTime out;
-- Add result
INSERT INTO #results (ID, tblName, tblID, BinaryData, RecTime)
VALUES (#ID, #tblName, #tblID, #BinaryData, #RecTime);
FETCH NEXT FROM files_cursor INTO #ID, #tblName, #tblID, #BinaryData;
END
-- Finalise
CLOSE files_cursor;
DEALLOCATE files_cursor;
-- Display the results from temporary table
SELECT * FROM #results;
Online demo: http://rextester.com/DXCK86463
This is the simplest way to do the above. No need of looping or any thing. You need dynamic code as Tables can be add at any time.
Note: In your sample data for Files table seems have wrong data in tblId ?
So I am changing your data to match IDs to respective tables.
Schema:
CREATE TABLE Table1 (
[ID] [bigint]
, [RecTime] [datetime]
)
CREATE TABLE Table2 (
[ID] [bigint]
, [RecTime] [datetime]
)
CREATE TABLE Table3 (
[ID] [bigint]
, [RecTime] [datetime]
)
CREATE TABLE Files (
[ID] [bigint]
, [tblName] nvarchar(255) NULL
, [tblID] bigint NULL
, [BinaryData] varbinary(max)
/* and some other columns */
)
INSERT INTO Table1 (
[ID]
, [RecTime]
)
SELECT '1', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '2', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '3', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '4', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '5', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
INSERT INTO Table2 (
[ID]
, [RecTime]
)
SELECT '11', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '12', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '13', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '14', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '15', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
INSERT INTO Table3 (
[ID]
, [RecTime]
)
SELECT '21', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '22', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '23', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '24', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '25', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
INSERT INTO Files (
[ID]
, [tblName]
, [tblID]
, [BinaryData]
)
SELECT '1', 'Table1', '1', 0x010203040506
UNION ALL SELECT '2', 'Table1', '2', 0x010203040506
UNION ALL SELECT '3', 'Table1', '2', 0x010203040506
UNION ALL SELECT '4', 'Table1', '3', 0x010203040506
UNION ALL SELECT '5', 'Table1', '4', 0x010203040506
UNION ALL SELECT '6', 'Table1', '5', 0x010203040506
UNION ALL SELECT '7', 'Table1', '5', 0x010203040506
UNION ALL SELECT '8', 'Table2', '11', 0x010203040506
UNION ALL SELECT '9', 'Table2', '11', 0x010203040506
UNION ALL SELECT '10', 'Table2', '12', 0x010203040506
UNION ALL SELECT '11', 'Table2', '13', 0x010203040506
UNION ALL SELECT '12', 'Table2', '14', 0x010203040506
UNION ALL SELECT '13', 'Table2', '12', 0x010203040506
UNION ALL SELECT '14', 'Table2', '15', 0x010203040506
UNION ALL SELECT '15', 'Table3', '21', 0x010203040506
UNION ALL SELECT '16', 'Table3', '22', 0x010203040506
UNION ALL SELECT '17', 'Table3', '24', 0x010203040506
UNION ALL SELECT '18', 'Table3', '23', 0x010203040506
UNION ALL SELECT '19', 'Table3', '25', 0x010203040506
UNION ALL SELECT '20', 'Table3', '25', 0x010203040506
UNION ALL SELECT '21', 'Table3', '21', 0x010203040506
Now your Dynamic Query Part:
DECLARE #QRY VARCHAR(MAX)='', #Tables VARCHAR(MAX)='';
--Capturing List of Table names for selecting RecTime
SELECT #Tables = #Tables+ tblName+'.RecTime,' FROM (
SELECT DISTINCT tblName FROM Files
)A
--To remove last comma
SELECT #Tables = SUBSTRING(#Tables,1, LEN(#Tables)-1)
--Preparing Dynamic Qry
SELECT #QRY = '
SELECT Files.ID,Files.BinaryData
,COALESCE('+#Tables+') AS RecTime
FROM Files '
SELECT #QRY =#QRY+ JOINS FROM (
SELECT DISTINCT '
LEFT JOIN '+ tblName + ' ON Files.tblID = '+tblName+'.ID AND Files.tblName= '''+tblName+''''
as JOINS
FROM Files
)A
print #QRY
EXEC( #QRY)
If you want to see what #Qry contains
/*
Print Output:
SELECT Files.ID,Files.BinaryData
,COALESCE(Table1.RecTime,Table2.RecTime,Table3.RecTime) AS RecTime
FROM Files
LEFT JOIN Table1 ON Files.tblID = Table1.ID AND Files.tblName= 'Table1'
LEFT JOIN Table2 ON Files.tblID = Table2.ID AND Files.tblName= 'Table2'
LEFT JOIN Table3 ON Files.tblID = Table3.ID AND Files.tblName= 'Table3'
*/
Try the following.
Select res.* , F.* From Files F
Left join
(
Select 'table1' as tablename, a.* From table1 a
Union
Select 'table2' as tablename, b.* From table2 b
Union
Select 'table3' as tablename, c.* From table3 c
)Res
On res.tablename = F.tblname
This design is just a way to model a hierarchy in ER. You basically have a physically partitioned table based on table names (ie Table1, Table2 and so on).
As such the easiest way to join these tables is to create a partitioned view and then join it.
In the case of your example you just have to do:
CREATE VIEW vmAll AS
SELECT 'Table1' AS 'tblName', [ID], [RecTime] FROM Table1
UNION ALL
SELECT 'Table2' AS 'tblName', [ID], [RecTime] FROM Table2
UNION ALL
SELECT 'Table3' AS 'tblName', [ID], [RecTime] FROM Table3;
GO
Now just join it with the Files table as usual (remember to specify the partitioning field too):
For example this:
SELECT
F.[ID]
, F.[tblName]
, F.[tblID]
, F.[BinaryData]
, A.RecTime
FROM [Files] F
LEFT OUTER JOIN vmAll A ON
F.[ID] = A.[ID] AND
F.tblName = A.tblName
Gives the expected result:
Notice an important thing: since it's a partitioned view SQL Server is able to perform partition elimination thus speeding up the join considerably (the correct term here should be table elimination).
For example the previous execution plan was:
If we add a filter predicate on the partitioning column:
SELECT
F.[ID]
, F.[tblName]
, F.[tblID]
, F.[BinaryData]
, A.RecTime
FROM [Files] F
LEFT OUTER JOIN vmAll A ON
F.[ID] = A.[ID] AND
F.tblName = A.tblName
WHERE A.tblName = 'Table1'
We will get this execution plan (notice two tables are not scanned at all):
Of course in order to use the partitioned view you have to be able to create it first. You can do it programmatically looking for the specific fields with a query like this one:
;WITH CTE AS
(
SELECT C.object_id FROM sys.columns C
INNER JOIN sys.objects O ON C.object_id = O.object_id
WHERE
(C.[name] = 'ID' OR C.[name] = 'RecTime')
AND O.[type] = 'U'
GROUP BY C.object_id
HAVING COUNT(*) = 2
)
SELECT OBJECT_NAME(object_id), object_id FROM CTE;
Have a look on below links. this might solve your problem.
MySQL join tables where table name is a field of another table ,
MySQL join tables where table name is a field of another table
If you have only few tables, then you can just do this, It could be slightly faster because it avoids Dynamic SQL.
Look at other solutions(I like Steve chamber's solution) here if you can't tell how many tables there will be or if there will be too many.
SELECT F.*, RecTime =
CASE tblName
WHEN 'Table1' THEN COALESCE(T1.RecTime, NULL)
WHEN 'Table2' THEN COALESCE(T2.RecTime, NULL)
WHEN 'Table3' THEN COALESCE(T3.RecTime, NULL)
ELSE NULL
END
FROM #Files F
LEFT JOIN #Table1 T1 ON F.tblID = T1.ID
LEFT JOIN #Table2 T2 ON F.tblID = T2.ID
LEFT JOIN #Table3 T3 ON F.tblID = T3.ID
Demo : http://rextester.com/FWWD90002

Switched from DAO to ADO and SQL query now returns an error, any ideas?

I was using the following code to query my database in DAO, which worked fine:
SELECT *
FROM (Resources LEFT JOIN [Select * FROM AvailabilityBlocks LEFT JOIN Location ON AvailabilityBlocks.LocationID=Location.LocationID WHERE ((CStr(AvailabilityBlocks.LocationID) IN ('8', '14', '16', '1', '15', '17', '10', '9', '19', '12', '5', '18', '13', '20', '3', '26', '2', '25', '28', '27') AND (AvailabilityBlocks.Type = 3 OR AvailabilityBlocks.Type = 4)) OR AvailabilityBlocks.Type = 2) AND Begin < #15-Jul-2013 12:00:00 AM# And Begin >= #08-Jul-2013 12:00:00 AM#]. AS FilteredTable ON Resources.ResourceID=FilteredTable.ResourceID) LEFT JOIN EmployeeTypes ON EmployeeTypes.TypeID=Resources.EmployeeType ORDER BY RClass, Resources.LastName ASC, Resources.FirstName ASC, Resources.ResourceID ASC, AvailabilityBlocks.Begin ASC, AvailabilityBlocks.End Desc, Location.SubType DESC
I then converted all my code to ADO and the SQL stopped working and now shows an
Syntax error in FROM clause.
error message!
Any help would be appreciated!
Try this.
I think the way your are closing out your derived table ("FilteredTable") is off.
Also. You might want to try a table alias...
SELECT *
FROM
Resources res
LEFT JOIN (
[Select * FROM AvailabilityBlocks avb LEFT JOIN Location loc ON avb.locID=loc.locID
WHERE ((CStr(avb.locID) IN ('8', '14', '16', '1', '15', '17', '10', '9', '19', '12', '5', '18', '13', '20', '3', '26', '2', '25', '28', '27')
AND (avb.Type = 3 OR avb.Type = 4)) OR avb.Type = 2)
AND Begin < #15-Jul-2013 12:00:00 AM# And Begin >= #08-Jul-2013 12:00:00 AM#] ) AS FilteredTable
ON res.ResourceID=FilteredTable.ResourceID)
LEFT JOIN EmployeeTypes ON EmployeeTypes.TypeID=res.EmployeeType
ORDER BY RClass, res.LastName ASC, res.FirstName ASC, res.ResourceID ASC, avb.Begin ASC, avb.End Desc, loc.SubType DESC

Adding multiple rows in SQL

I am asked to add 8 rows into a table.
insert into Rating ( rID, mID, stars, ratingDate )
values ('207', '101', '5', null), ('207', '102', '5', null),
('207', '103', '5', null), ('207', '104', '5', null),
('207', '105', '5', null), ('207', '106', '5', null),
('207', '107', '5', null), ('207', '108', '5', null)
This operation works good with one value added but when adding multiple gives the error
Query failed to execute: near ",": syntax error
What is missing?
A late answer
If your are using SQLITE version 3.7.11 or above, then multiple rows insert is possible by this syntax,
SIMPLEST WAY
INSERT INTO Rating (rID, mID, stars, ratingDate) VALUES ('207', '102', '5', null) , ('207', '102', '5', null) , ('207', '102', '5', null)
The above clause posted in question do work if the new SQLITE version is used.
SELECT CLAUSE
insert into Rating
SELECT '207' AS rID, '101' AS mID, '5' AS stars, null AS ratingDate
UNION SELECT '207', '102', '5', null
UNION SELECT '207', '103', '5', null
UNION SELECT '207', '104', '5', null
UNION SELECT '207', '105', '5', null
UNION SELECT '207', '106', '5', null
UNION SELECT '207', '107', '5', null
UNION SELECT '207', '108', '5', null
or SQL is
insert into Rating (rID, mID, stars, ratingDate)
SELECT '207', '101', '5', null
UNION SELECT '207', '102', '5', null
UNION SELECT '207', '103', '5', null
UNION SELECT '207', '104', '5', null
UNION SELECT '207', '105', '5', null
UNION SELECT '207', '106', '5', null
UNION SELECT '207', '107', '5', null
UNION SELECT '207', '108', '5', null
REMEMBER I you do not want to check for duplicate in above set of inserted values then use UNION ALL in place of UNION as it will be little faster.
I assume your RDBMS don't support such construction.
insert into Rating ( rID, mID, stars, ratingDate )
values ('207', '101', '5', null);
insert into Rating ( rID, mID, stars, ratingDate )
values ('207', '102', '5', null);
.....
I sugest:
insert into Rating ( rID, mID, stars, ratingDate ) values ('207', '101', '5', null);
insert into Rating ( rID, mID, stars, ratingDate ) values ('207', '102', '5', null);
...
insert into Rating ( rID, mID, stars, ratingDate ) values ('207', '108', '5', null);
i created table in sql lite . table creation script is as follows
create table Rating (rID varchar(10),mID varchar(10),stars varchar(10),ratingDate date);
And i used following query to insert into above table and its working fine for me.
insert into Rating ( rID, mID, stars, ratingDate )
values ('207', '101', '5', null), ('207', '102', '5', null),
('207', '103', '5', null), ('207', '104', '5', null),
('207', '105', '5', null), ('207', '106', '5', null),
('207', '107', '5', null), ('207', '108', '5', null);