Full Outer Join returning too many rows - sql

What I'm trying to achieve is a data set where by I have a set of holdings data against its benchmark and show where the holdings are either on or off benchmark. The data set should look like this:
PositionDate PortfolioCode Benchmark SecurityDesciption PortfolioWeight BenchmarkWeight
2017-03-31 Port1 JPM Sec1 1.00 1.5
2017-03-31 Port1 JPM Sec2 0.54 NULL
2017-03-31 Port1 JPM Sec3 NULL 0.5
My SQL currently looks like the following with a FULL OUTER JOIN so that you can return weights from both the portfolio data and the benchmark data depending on whether the portfolio holds it or not:
SELECT
lt.PositionDate AS PositionDate
, lt.PortfolioCode AS PortfolioCode
, gpi.indx_desc AS Benchmark
, i.iss_desc AS SecurityDescription
, lt.BaseCCYEffectiveExposure / NULLIF(lt.TOTALPortfolioMV_BaseCcy,0) AS PortfolioWeight
, issindx.WGHT_EOD AS BenchmarkWeight
FROM #Lkthru AS lt
INNER JOIN ISSUE_DG AS i
ON i.INSTR_ID = lt.ID_NETIK_INSTR_ID
INNER JOIN GP_BNCHMRK_XREF AS bxref -- Portfolio Benchmark Crossreference
ON bxref.acct_id = lt.portfoliocode -- Links to Portfolio
AND bxref.bnchmrk_type = 'Primary' -- Link to reporting benchmark
AND (#EndDate BETWEEN bxref.start_tms AND ISNULL(bxref.end_tms,#EndDate)) -- Ensures you pick up the correct benchmark for the date you are reporting for
INNER JOIN gp_index AS gpi
ON gpi.indx_id = bxref.indx_id -- Links to GP_INDEX to pick up the correct benchmark
FULL OUTER JOIN iss_indx_cnstnt AS issindx -- Join to ensure we have all constituents of benchmark returned as well as all portfolio constituents
ON issindx.indx_instr_id = gpi.INSTR_ID
AND issindx.cnstnt_instr_id = i.instr_id -- Join to constituents of portfolio
AND issindx.as_of_dte = #EndDate
However I'm not returning the desired result set and I seem to be getting every record from ISS_INDX_CNSNT returned. It must be something to do with the filters after the ON on my FULL OUTER JOIN or its just not possible to do what I'm trying to achieve? I'm trying to avoid having to put the filtered data into a temp table and then join or creating a UNION ALL and then GROUP BY.
Does anyone have an ideas as to where I'm going wrong?
EDIT - included working example
DECLARE #EndDate DATETIME
SET #EndDate = '2017-03-31'
DECLARE #Port TABLE
(
PortCode VARCHAR(40)
)
INSERT INTO #Port VALUES ('Port1')
DECLARE #Lkthru TABLE
(
PositionDate DATETIME
, PortfolioCode VARCHAR(40)
, Instr_ID VARCHAR(40)
, PortfolioWeight FLOAT
)
INSERT INTO #Lkthru VALUES('2017-03-31','Port1','1',1.00)
INSERT INTO #Lkthru VALUES('2017-03-31','Port1','2',0.54)
DECLARE #Issue_DG TABLE
(
Instr_ID VARCHAR(40)
, SecurityDesc VARCHAR(100)
)
INSERT INTO #Issue_DG VALUES ('1','Sec1')
INSERT INTO #Issue_DG VALUES ('2','Sec2')
INSERT INTO #Issue_DG VALUES ('3','Sec3')
INSERT INTO #Issue_DG VALUES ('4','Sec4')
DECLARE #GP_BNCHRK_XREF TABLE
(
PortfolioCode VARCHAR(40)
, BenchmarkID VARCHAR(40)
)
INSERT INTO #GP_BNCHRK_XREF VALUES ('Port1','JPM1')
DECLARE #GPIndex TABLE
(
BenchmarkID VARCHAR(40)
, BenchmarkNme VARCHAR(40)
)
INSERT INTO #GPIndex VALUES ('JPM1','JPM')
DECLARE #IssueIndexConst TABLE
(
BenchmarkDate DATETIME
, BenchmarkID VARCHAR(40)
, Const_ID VARCHAR(40)
, BenchmarkWeight FLOAT
)
INSERT INTO #IssueIndexConst VALUES ('2017-03-31','JPM1','1',1.5)
INSERT INTO #IssueIndexConst VALUES ('2017-03-31','JPM1','3',0.5)
INSERT INTO #IssueIndexConst VALUES ('2017-03-31','JPM1','4',0.5)
INSERT INTO #IssueIndexConst VALUES ('2017-02-28','JPM1','1',1.5)
INSERT INTO #IssueIndexConst VALUES ('2017-02-28','JPM1','3',0.5)
INSERT INTO #IssueIndexConst VALUES ('2017-02-28','JPM1','4',0.5)
SELECT
ISNULL(lt.PositionDate,issindx.BenchmarkDate) AS PositionDate
, ISNULL(lt.PortfolioCode,bxref2.PortfolioCode) AS PortfolioCode
, ISNULL(gpi.BenchmarkNme,gpi2.BenchmarkNme) AS Benchmark
, ISNULL(i.SecurityDesc,ib.SecurityDesc) AS SecurityDesc
, lt.PortfolioWeight
, issindx.BenchmarkWeight
FROM #GP_BNCHRK_XREF AS bxref
INNER JOIN #Lkthru AS lt
ON lt.PortfolioCode = bxref.PortfolioCode
INNER JOIN #Issue_DG AS i
ON i.Instr_ID = lt.Instr_ID
INNER JOIN #GPIndex AS gpi
ON gpi.BenchmarkID = bxref.BenchmarkID
FULL OUTER JOIN #IssueIndexConst AS issindx
ON issindx.BenchmarkID = gpi.BenchmarkID
AND issindx.Const_ID = i.Instr_ID
AND issindx.BenchmarkDate = #EndDate
LEFT OUTER JOIN #Issue_DG AS ib
ON ib.Instr_ID = issindx.Const_ID
LEFT OUTER JOIN #GPIndex AS gpi2
ON gpi2.BenchmarkID = issindx.BenchmarkID
LEFT OUTER JOIN #GP_BNCHRK_XREF AS bxref2
ON bxref2.BenchmarkID = issindx.BenchmarkID

Related

SQL Query - Run query multiple times but with a different variable date

I have a lengthy query written in SQL that uses CTEs and multiple variables to produce a report of about 1500 customer records with many columns based on a particular date, #ToDate. Some of the tables are ordered CTEs so I only get the latest value based on the #ToDate.
I've omitted specifics but the structure is as follows:
Declare #ToDate date .....
Declare #Category varchar ....;
with cte1 as (select * from table1 where table1.start_date <= #ToDate and (table1.end_date > #ToDate or table1.end_date is null))
,cte2 as (select * from table2 where table2.start_date <= #ToDate and (table2.end_date > #ToDate or table2.end_date is null))
select * from cte1
left join cte2 on cte2.id = cte1.id
where .....
which gives me the following results
|RunDate |CustomerID|DOB |Category|Col5 |Col6 |
|----------|----------|----------|--------|------|------|
|2021-08-30|11111 |2000-01-01|Cat1 | | |
|2021-08-30|22222 |2000-02-02|Cat2 | | |
I'd like to run the same script multiple times but with a different date. So run with #ToDate = '2021-08-30' which gives me one set of results and then every past Monday n number of times which would give me results like this...
|RunDate |CustomerID|DOB |Category|Col5 |Col6
|----------|----------|----------|--------|------|------|
|2021-08-30|11111 |2000-01-01|Cat1 | | |
|2021-08-30|22222 |2000-02-02|Cat2 | | |
|2021-08-23|11111 |2000-01-01|Cat1 | | |
|2021-08-23|22222 |2000-02-02|Cat2 | | |
|2021-08-23|33333 |2000-03-03|Cat9 | | |
I do have a calendar table available so I can easily identify the past n Mondays (or other day I like).
The only variable to change is the #ToDate as this is the Run Date, or As At Date if you will. Essentially I want to run it multiple times for the past few Mondays so I can get what the results were like at 30-08, 23-08, 16-08 etc...
I've never used loops and research suggests I should maybe avoid them or use them as a last resort. I'm not sure on the best approach and if I do use loops, how I wrap it around my query.
Thanks in advance
The question really needs a bit more elaboration but I have give a guess at what you are trying to do with this example.
I have create a Customers and Orders table and then display the results for the date range
I don't think you need to loop with cursors and such as you can get the loop effect by just using the #DateRanges and join on that. it being a CTE or not.
Please let me know if this is not what you meant and I will remove the answer
-- Setup a temp table to hold the dates I want to look for
IF EXISTS (SELECT * FROM tempdb.dbo.sysobjects O WHERE O.xtype in ('U') AND O.id = object_id(N'tempdb..#DateRanges'))
BEGIN
PRINT 'Removing temp table #DateRanges'
DROP TABLE #DateRanges;
END
CREATE TABLE #DateRanges (
[Date] DATE
)
-- Add some dates
INSERT INTO #DateRanges ([Date])
VALUES ('2021-08-30'),
('2021-08-23'),
('2021-08-16')
-- Setup some customers
IF EXISTS (SELECT * FROM tempdb.dbo.sysobjects O WHERE O.xtype in ('U') AND O.id = object_id(N'tempdb..#Customers'))
BEGIN
PRINT 'Removing temp table #Customers'
DROP TABLE #Customers;
END
CREATE TABLE #Customers (
CustomerId BIGINT IDENTITY(1,1) NOT NULL,
[Name] NVARCHAR(50),
DOB DATE NOT NULL,
CONSTRAINT PK_CustomerId PRIMARY KEY (CustomerId)
)
INSERT INTO #Customers ([Name], DOB)
VALUES('Bob', '1989-01-01'),
('Robert', '1994-01-01'),
('Andrew', '1992-01-01');
-- Setup some orders
IF EXISTS (SELECT * FROM tempdb.dbo.sysobjects O WHERE O.xtype in ('U') AND O.id = object_id(N'tempdb..#Order'))
BEGIN
PRINT 'Removing temp table #Order'
DROP TABLE #Order;
END
CREATE TABLE #Order (
OrderId BIGINT IDENTITY(1,1) NOT NULL,
CustomerId BIGINT NOT NULL,
CreatedDate DATE NOT NULL,
Category NVARCHAR(50) NOT NULL,
CONSTRAINT PK_OrderId PRIMARY KEY (OrderId)
)
INSERT INTO #Order(CustomerId, CreatedDate, Category)
VALUES
(1, '2021-08-30', 'Cat1'),
(1, '2021-08-23', 'Cat2'),
(2, '2021-08-30', 'Cat1'),
(2, '2021-08-23', 'Cat2'),
(2, '2021-08-16', 'Cat3'),
(3, '2021-08-30', 'Cat1'),
(3, '2021-08-16', 'Cat2')
-- Using the #DateRanged temp table we can the use this to ge the data we need so no need for a loop
SELECT *
FROM #DateRanges AS DR
LEFT JOIN #Order AS O ON O.
CreatedDate <= DR.[Date] AND O.CreatedDate >= DATEADD(D, -6, DR.[Date])

How to capture columns from joined tables using output clause?

I am updating a table called tableA by joining tableB and tableC at the same time I am capturing updated records into temp table using output clause from tableA . Now I want capture columns from table B for the updated data but output clause isn't allowing the same.
Eg:
Update SLC Set SLC.Datascrublevel = C.Datascrublevel
OUTPUT [Deleted].Systemcode,
[Deleted].Systemkey,
[Deleted].datascrublevel,
[Inserted].datascrublevel
INTO #TEMP1
FROM TABLEA SLC with(nolock)
INNER JOIN TABLEB SC ON SC.SystemCode = SLC.SystemCode
INNER JOIN TABLEC SL ON SL.SystemCode = SLC.SystemCode and SLC.SystemKey = SL.Systemkey
INNER JOIN #TEMP C ON SLC.Datascrublevel <> C.DataScrubLevel AND C.Systemcode = SLC.SystemCode and C.Systemkey = SLC.SystemKey
Now I want columns from tableB to capture into temp table using output clause. Please provide your advise if there are any alternative ways.
Just Like you have given it as [deleted].[Column Name] and [Inserted].[Column Name] add one more column as [SC].[Column Name]
Example :
IF OBJECT_ID('TempDb..#TABLEA') IS NOT NULL
DROP TABLE #TABLEA
IF OBJECT_ID('TempDb..#TABLEB') IS NOT NULL
DROP TABLE #TABLEB
IF OBJECT_ID('TempDb..#TABLEC') IS NOT NULL
DROP TABLE #TABLEC
IF OBJECT_ID('TempDb..#TABLED') IS NOT NULL
DROP TABLE #TABLED
CREATE TABLE #TABLEA
(
SeqNo INT IDENTITY(1,1),
MyDate DATE
)
CREATE TABLE #TABLEB
(
SeqNo INT IDENTITY(1,1),
FullName VARCHAR(20)
)
CREATE TABLE #TABLEC
(
SeqNo INT IDENTITY(1,1),
FullName VARCHAR(20),
MyDate DATE
)
CREATE TABLE #TABLED
(
SeqNo INT,
MyDate DATE,
FullName VARCHAR(20)
)
INSERT INTO #TABLEA
(
MyDate
)
SELECT GETDATE()
UNION
SELECT GETDATE()+1
UNION
SELECT GETDATE()-1
INSERT INTO #TABLEB
(
FullName
)
VALUES('A'),('B'),('C')
INSERT INTO #TABLEC
(
FullName
)
VALUES('A'),('B'),('C')
UPDATE C
SET MyDate = A.MyDate
OUTPUT
deleted.SeqNo,
deleted.MyDate,
B.FullName
INTO #TABLED
FROM #TABLEC C
INNER JOIN #TABLEB B
ON C.FullName = B.FullName
INNER JOIN #TABLEA A
ON A.SeqNo = B.SeqNo
SELECT * FROM #TABLED

Converting strange SQL Server JOIN syntax to MySQL syntax

I have a SQL Server query that I am attempting to port to MySQL, but the JOIN syntax is something that I have never seen used before. The query is from a view designed to measure procedure code usage. What the heck is going on with the JOIN syntax just past T.PatID = P.ID, and the third LEFT OUTER JOIN, and what equivalent syntax can we use in MySQL? It does not like this JOIN syntax at all (disregard the ISNULL and CONVERT SQL Server specific syntax)
SELECT
T.Code
, P.LastName
, P.FirstName
, T.TranDate
, CD.DaysUnits
, T.TranAmt
, TD.FullName AS Provider
, ISNULL(TD.ID, ISNULL(AD.ID, PD.ID)) AS DoctorID
FROM
dbo.Doctors AS PD
INNER JOIN
dbo.Transactions AS T
INNER JOIN
dbo.Patients AS P
ON
T.PatID = P.ID
ON
PD.ID = P.DoctorID
LEFT OUTER JOIN
dbo.Doctors AS TD
ON
T.DoctorID = TD.ID
LEFT OUTER JOIN
dbo.Doctors AS AD
LEFT OUTER JOIN
dbo.Appointments
ON
AD.ID = dbo.Appointments.DoctorID
AND CONVERT(varchar(20), dbo.Appointments.ScheduleDateTime, 8) <> '00:00:00'
ON
T.ApptID = dbo.Appointments.ID
LEFT OUTER JOIN
dbo.ChargeDetails AS CD
ON
T.ID = CD.ChargeTranID
WHERE
(
T.Code IS NOT NULL
)
The SHOW CREATE TABLE are as follows
CREATE TABLE Doctors
(
ID int(10) NOT NULL PRIMARY KEY
, FullName varchar(50) DEFAULT NULL
)
CREATE TABLE Patients
(
LName varchar(50) DEFAULT NULL
, FName varchar(50) DEFAULT NULL
, ID int(10) NOT NULL PRIMARY KEY
)
CREATE TABLE Transactions
(
TranType varchar(2) DEFAULT NULL
, Code varchar(100) DEFAULT NULL
, TranSubType varchar(2) DEFAULT NULL
, Description varchar(2000) DEFAULT NULL
, TranDate datetime
, PatID int(10) DEFAULT NULL
, ID int(10) NOT NULL PRIMARY KEY
, TranAmt decimal(19,4) DEFAULT NULL
, ApptID int(10) DEFAULT NULL
, DoctorID int(10) DEFAULT NULL
)
CREATE TABLE ChargeDetails
(
DaysUnits varchar(50) DEFAULT NULL
-- DaysUnits is just an int ranging from 1 to 2
, ChargeTranID int(10) NOT NULL PRIMARY KEY
)
CREATE TABLE Appointments
(
DoctorID int(10) DEFAULT NULL
, PatientID int(10) DEFAULT NULL
, ScheduleDateTime datetime DEFAULT NULL
, ID int(10) NOT NULL PRIMARY KEY
)
Thank you in advance for your help.
Here is a similar (and simplified) query using the same structure as the first query. The second query moves the joins around to make things easier to read.
set nocount on;
use tempdb;
go
declare #doc table (id int not null);
declare #tran table (id int not null, patid int not null);
declare #patients table (id int not null, docid int not null);
insert #doc (id) values (1);
insert #patients (id, docid) values (25, 1);
insert #tran (id, patid) values (100, 25)
select *
from #doc as pd
inner join #tran as t
inner join #patients as p
on t.patid = p.id
on pd.id = p.docid;
select *
from #tran as t
inner join #patients as p
on t.patid = p.id
inner join #doc as pd
on pd.id = p.docid;
Other things look strange. I don't see a need to join to appointments but I'm not going to spend a lot of time to figure out the logic and the schema. The convert usage seems like a bad way to check for null - unless there is a special "flag" datetime value that is used as the equivalent to null. Again, you need to understand the query, the goal of the query, the schema on which it is based, and how the tables are populated. Quite frankly, this code raises concerns about the quality of the entire system.
.

How can I pull data from multiple sql tables using a join statement

I'm designing a simple in-office ticket system, and would like to include a field for the party responsible for the next action. To do so right this moment I'm thinking of using tableName and tableID as specifiers for the specific responsible party (could be a technician, customer, or third party, all in different tables)
It would be fine to pull that data in and run another select call using the name of the table as a parameter, but the extra data flow slows things down significantly.
Is there a way to use a single join statement to return the details of the party with a column for the table name and one for the individual table id or is there a better way to store the data from multiple potential tables?
You can use left join to achieve your requirement :-
Set Nocount On;
Declare #OfficeTickets Table
(
Id Int Identity(1,1)
,Column1 Varchar(100)
,PartyType Varchar(1)
,TechnicianId Int Null
,CustomerId Int Null
,ThirdPartyId Int Null
)
Declare #OfficeTickets1 Table
(
Id Int Identity(1,1)
,Column1 Varchar(100)
,TableName Varchar(100)
,TableId Int Null
)
Declare #Technician Table
(
Id Int Identity(1,1)
,TechnicianName Varchar(100)
)
Declare #Customers Table
(
Id Int Identity(1,1)
,CustomerName Varchar(100)
)
Declare #ThirdParty Table
(
Id Int Identity(1,1)
,ThirdPartyName Varchar(100)
)
Insert Into #Technician(TechnicianName) Values
('Technician_1')
,('Technician_2')
,('Technician_3')
Insert Into #Customers(CustomerName) Values
('Customer_1')
,('Customer_2')
,('Customer_3')
Insert Into #ThirdParty(ThirdPartyName) Values
('ThirdParty_1')
,('ThirdParty_2')
,('ThirdParty_3')
,('ThirdParty_4')
Insert Into #OfficeTickets(Column1,PartyType,TechnicianId,CustomerId,ThirdPartyId) Values
('ABC','T',3,Null,Null)
,('XYZ','C',Null,2,Null)
,('PUQ','P',Null,Null,4)
Insert Into #OfficeTickets1(Column1,TableName,TableId) Values
('ABC','Technician',3)
,('XYZ','Customers',2)
,('PUQ','ThirdParty',4)
---- taken separate columns for parties
Select ot.Id
,ot.Column1
,t.TechnicianName
,c.CustomerName
,tp.ThirdPartyName
From #OfficeTickets As ot
Left Join #Technician As t On ot.PartyType = 'T' And ot.TechnicianId = t.Id
Left Join #Customers As c On ot.PartyType = 'C' And ot.CustomerId = c.Id
Left Join #ThirdParty As tp On ot.PartyType = 'P' And ot.ThirdPartyId = tp.Id
---- by TableName and TableId
Select ot.Id
,ot.Column1
,t.TechnicianName
,c.CustomerName
,tp.ThirdPartyName
From #OfficeTickets1 As ot
Left Join #Technician As t On ot.TableName = 'Technician' And ot.TableId = t.Id
Left Join #Customers As c On ot.TableName = 'Customers' And ot.TableId = c.Id
Left Join #ThirdParty As tp On ot.TableName = 'ThirdParty' And ot.TableId = tp.Id
output:-

INSERT from JOIN inserts null values

The need to insert values from 3 tables into another table called myTable. The required fields in myTable are:
[Id_student] int,
[id_subjects] int
[degrees] float
[st_Name] nvarchar(30)
[Id_Class] int
[Id_Group] int
[Class] nvarchar(15)
[group] nvarchar(15))` ..
I created the stored procedure below. But after viewing the table I found that only the passed parameters were saved! ie #Id_student , #id_subjects , #degrees. Can anyone explain what is wrong with this code?
CREATE storedprocedure mySp_myTable_insert
#Id_student int,
#id_subjects int,
#degrees int
as
DECLARE #st_Name nvarchar(30)
SELECT #st_Name = pst.st_Name FROM dbo.sudents AS pst where pst.id_student=#id_student ;
INSERT [myTable]
(
[Id_student],
[id_subjects],
[degrees],
[st_Name],
[Id_Class],
[Id_Group],
[Class],
[group]
)
(select
#Id_student,
#id_subjects,
#degrees,
#st_Name
,tc.Id_Class
,tg.Id_Group
,tc.Class
,tg.group
from dbo.subjects sbj
inner join tGroup tg
inner join tClass tc
on tc.Id_Class=tg.Id_Class
on sbj.Id_Group =tg.Id_Group
where sbj.id_subjects=#id_subjects)
I think you should drop the parentheses around the SELECT statement and fix the order of the join-on keywords/clauses.
Try this version:
CREATE storedprocedure mySp_myTable_insert
#Id_student int,
#id_subjects int,
#degrees int
as
DECLARE #st_Name nvarchar(30)
SELECT #st_Name = pst.st_Name FROM dbo.sudents AS pst where pst.id_student=#id_student ;
INSERT [myTable]
(
[Id_student],
[id_subjects],
[degrees],
[st_Name],
[Id_Class],
[Id_Group],
[Class],
[group]
)
select
#Id_student,
#id_subjects,
#degrees,
#st_Name
,tc.Id_Class
,tg.Id_Group
,tc.Class
,tg.group
from dbo.subjects sbj
inner join tGroup tg on sbj.Id_Group =tg.Id_Group
inner join tClass tc on tc.Id_Class=tg.Id_Class
where sbj.id_subjects=#id_subjects
GO