Need help implementing a Full Outer Join in MS Access - sql-server

I'm having trouble getting a query to work properly in Access. I need a full outer join on dbo_cardpurchases and dbo_vendors so that all all vendors will appear in the query regardless of whether a purchase was made at that vendor. But Access doesn't support full outer joins. How else can I do this?
SELECT dbo_vendors.name,
Iif([fundingsourceid] = 10, [amount], "") AS Credit,
Iif(( [fundingsourceid] = 2 )
OR ( [fundingsourceid] = 3 ), [amount], "") AS EBT,
Iif([fundingsourceid] = 4, [amount], "") AS [Match],
dbo_cardpurchases.updateddate,
dbo_markets.marketid
FROM (((dbo_cardpurchases
LEFT JOIN dbo_vendors
ON dbo_cardpurchases.vendorid = dbo_vendors.vendorid)
LEFT JOIN dbo_cardfundings
ON dbo_cardpurchases.cardfundingid =
dbo_cardfundings.cardfundingid)
INNER JOIN dbo_marketevents
ON dbo_cardpurchases.marketeventid =
dbo_marketevents.marketeventid)
INNER JOIN dbo_markets
ON dbo_marketevents.marketid = dbo_markets.marketid
ORDER BY dbo_vendors.name;

As mentioned in the Wikipedia article on joins here, for sample tables
[employee]
LastName DepartmentID
---------- ------------
Heisenberg 33
Jones 33
Rafferty 31
Robinson 34
Smith 34
Williams NULL
and [department]
DepartmentID DepartmentName
------------ --------------
31 Sales
33 Engineering
34 Clerical
35 Marketing
the full outer join
SELECT *
FROM employee FULL OUTER JOIN department
ON employee.DepartmentID = department.DepartmentID;
can be emulated using a UNION ALL of three SELECT statements. So, in Access you could do
SELECT dbo_employee.LastName, dbo_employee.DepartmentID,
dbo_department.DepartmentName, dbo_department.DepartmentID
FROM dbo_employee
INNER JOIN dbo_department ON dbo_employee.DepartmentID = dbo_department.DepartmentID
UNION ALL
SELECT dbo_employee.LastName, dbo_employee.DepartmentID,
NULL, NULL
FROM dbo_employee
WHERE NOT EXISTS (
SELECT * FROM dbo_department
WHERE dbo_employee.DepartmentID = dbo_department.DepartmentID)
UNION ALL
SELECT NULL, NULL,
dbo_department.DepartmentName, dbo_department.DepartmentID
FROM dbo_department
WHERE NOT EXISTS (
SELECT * FROM dbo_employee
WHERE dbo_employee.DepartmentID = dbo_department.DepartmentID)
However, since you are using linked tables into SQL Server you can just use an Access pass-through query and perform a "real" FULL OUTER JOIN using T-SQL:
Pass-through queries always produce recordsets that are not updateable, but a native Access query against linked tables that uses UNION ALL is going to produce a recordset that is not updatable anyway, so why not take advantage of the speed and simplicity of just using SQL Server to run the query?

Related

SQL Query to get data from various databases

I wrote the below query to pull the data from different databases. I have created two temp tables to pull the data from two different databases and finally a select statement from the original database to join all the tables. My query is getting executed but not getting any data.(Report is blank). I tried executing the two temp tables separately. it is giving the correct data. But when I execute the whole query, the result is blank. Below is the query. Please help.
"set fmtonly off
use GODSDB
IF object_id('tempdb..#CISIS_Call_Log') IS NOT NULL DROP TABLE #CISIS_Call_Log
select *
into #CISIS_Call_Log
from OPENQUERY (CSISDB,
'select
ccl.ContractOID,
ccl.db_insertdate,
ccl.ContractCallLogStatusIdentifier,
ccl.db_UpdateDate,
ccp.ContractCallLogPurposeOID,
ccp.ContractCallLogPurposeIdentifier,
ccp.Description
from csisdb.dbo.ContractCallLog CCL
inner join csisdb.dbo.ContractCallLogPurpose CCP on ccl.ContractCallLogPurposeIdentifier = ccp.ContractCallLogPurposeIdentifier
where JurisdictionShortIdentifier = ''ON''
AND ContractCallLogStatusIdentifier IN (''DNR'', ''NR'')
')
IF object_id('tempdb..#CMS_Campaign') IS NOT NULL DROP TABLE #CMS_Campaign
select *
into #CMS_Campaign
from OPENQUERY (BA_GBASSTOCMS, '
Select
SystemSourceIdentifier,
ContractOID,
OfferSentDate,
CampaignOfferTypeIdentifier,
CampaignContractStatusIdentifier,
CampaignContractStatusUpdateDate,
DeclineDate,
CampaignOfferOID,
CampaignOID,
CampaignStartDate,
CampaignEndDate,
Jurisdiction,
CampaignDescription
from CMS.dbo.vw_CampaignInfo
where Jurisdiction = ''ON''
and CampaignOfferTypeIdentifier = ''REN''
')
select mp.CommodityTypeIdentifier as Commodity
,c.RtlrContractIdentifier as ContractID
,cs.ContractStatusIdentifier as ContractStatus
,c.SigningDate
,cf.StartDate as FlowStartDate
,cf.EndDate as FlowEndDate
,datediff(day, getdate(), c.RenewalDate) as RemainingDays
,c.RenewalDate
,l.ContractCallLogStatusIdentifier as CallLogType
,Substring (l.Description, 1, 20) as CallPurpose
,l.db_insertDate as CallLogDate
,cms.CampaignOfferOID as OfferID
,cms.CampaignContractStatusIdentifier as OfferStatus
,cms.CampaignContractStatusUpdateDate as StatusChangeDate
,cms.DeclineDate
from Contract c
inner join contractstate cs on cs.contractoid = c.ContractOID
and cs.ContractStatusIdentifier in ('ERA', 'FLW')
and datediff(day, getdate(), c.RenewalDate) > 60
inner join SiteIdentification si on si.SiteOID = c.SiteOID
inner join MarketParticipant mp on mp.MarketParticipantOID = si.MarketParticipantOID
inner join Market m on m.MarketOID = mp.MarketOID
inner join Jurisdiction j on j.JurisdictionOID = m.JurisdictionOID
and j.CountryCode = 'CA'
and j.ProvinceOrStateCode = 'ON'
inner join ContractFlow cf on cf.ContractOID = c.ContractOID
inner join #CISIS_Call_Log l on convert(varchar(15), l.ContractOID) = c.RtlrContractIdentifier
inner join #CMS_Campaign cms on convert(varchar(15), cms.ContractOID) = c.RtlrContractIdentifier
set fmtonly on"
IF the data in each temp table is verified, then:
Try a smaller, less complex, query to test your temp tables with. Also try them using a LEFT join as well e.g.:
select
c.RtlrContractIdentifier as ContractID
, c.SigningDate
, datediff(day, getdate(), c.RenewalDate) as RemainingDays
, c.RenewalDate
, l.ContractCallLogStatusIdentifier as CallLogType
, Substring (l.Description, 1, 20) as CallPurpose
, l.db_insertDate as CallLogDate
, cms.CampaignOfferOID as OfferID
, cms.CampaignContractStatusIdentifier as OfferStatus
, cms.CampaignContractStatusUpdateDate as StatusChangeDate
, cms.DeclineDate
from Contract c
LEFT join #CISIS_Call_Log l on convert(varchar(15), l.ContractOID) = c.RtlrContractIdentifier
LEFT join #CMS_Campaign cms on convert(varchar(15), cms.ContractOID) = c.RtlrContractIdentifier
Does this return data? Does it return data from both joined tables?
If neither temp table is returning data then those join conditions need to be changed.
If both temp tables do return data from that query, then try INNER joins. If that still works, then add back more joins (one at a time) until you identify the join that causes the overall fault.
Without data for every table it just isn't possible for us to pinpoint the exact reason for a NULL result. Only you can, so you need to trouble-shoot the problem one step at a time.

Create and execute stored procedure in SQL Server

I have four tables:
dbo.Projects (id, ProjectName, Areas, PaymentSystem, Districts.id, purpose.id, types.id, etc)
dbo.Districts(id, DistrictsName)
dbo.Purpose (id, PurposeName) - has residential & commercial
dbo.Types (id, typName)
I want to select DistrictsName where PurposeName = 'residential'
I tried this procedure :
CREATE PROCEDURE [dbo].[SearchResidentialProjects]
AS
SELECT
dbo.Projects.ID,
dbo.Districts.DistrictName,
dbo.Purpose.PurposeName
FROM
dbo.Projects
INNER JOIN
dbo.Purpose ON dbo.Projects.PurposeID = dbo.Purpose.ID
INNER JOIN
dbo.Districts ON dbo.Projects.DistrictID = dbo.Districts.ID
WHERE
dbo.Purpose.PurposeName = N'Residential'
this is the result from this procedure:
ID DistrictsName PurposeName
1 District1 residential
2 District1 residential
3 District2 residential
4 District2 residential
i want display the DistrictsName without duplicate or with different values , i a have also one more project per district in projects records . this what i want to display :
ID DistrictsName PurposeName
1 District1 residential
2 District2 residential
how i get this result ,
any help is appreciated.
Why do people use stored procedures when views are much more appropriate? I have never understood this. It seems peculiar to SQL Server users.
In any case, you can do what you want with aggregation:
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as id,
d.DistrictName, p.PurposeName
FROM dbo.Projects pr INNER JOIN
dbo.Purpose pu
ON pr.PurposeID = pu.ID INNER JOIN
dbo.Districts d
ON pr.DistrictID = d.ID
WHERE pu.PurposeName = N'Residential'
GROUP BY d.DistrictName, p.PurposeName;
The use of table aliases makes the query much easier to write and to read.
In addition, I don't understand the id column being output. Why would you want to construct a new id? In any case, that is what your data suggests.
Use DISTINCT statement for removing the duplicates:
CREATE PROCEDURE [dbo].[SearchResidentialProjects]
AS
SELECT DISTINCT
dbo.Projects.ID,
dbo.Districts.DistrictName,
dbo.Purpose.PurposeName
FROM
dbo.Projects
INNER JOIN
dbo.Purpose ON dbo.Projects.PurposeID = dbo.Purpose.ID
INNER JOIN
dbo.Districts ON dbo.Projects.DistrictID = dbo.Districts.ID
WHERE
dbo.Purpose.PurposeName = N'Residential'

T-SQL stored procedure with joins

I have a problem with a stored procedure. I have 3 tables for a mass mailing service and I want to know how many tasks (table - MMProcessItem) I still need to do...
I have these 3 tables:
Here is my select:
SELECT
MMAddress.AddressID, MMProcess.ProcessID
FROM
MMProcess, MMAddress
LEFT OUTER JOIN
(SELECT *
FROM MMProcessItem) Items ON Items.AddressID = MMAddress.AddressID
WHERE
Items.ResultID IS NULL
ORDER BY
ProcessID, AddressID
And my SQL Code is working fine if there is nothing in MMProcessItem table, this is what I get:
But if I send 1 email, like the one with AddressID = 1 and ProcessID = 1, I don't get anymore the 1 record with AddressID = 1 and ProcessID = 2, I should get a total of 3 records, but what i get is a total of 2 records...
Sorry if this is an amateur mistake, im not used to work with t-sql and do these type of things...
Your join to MMProcessItem requires two predicates, one to join to MMProcess, and one to join to MMAddress. You are currently only joining to MMAddress. That means that when you add a record with AddressID = 1 and ProcessID = 1 it removes both records where AddressID = 1, not just the one record where AddressID is 1 and ProcessID is 1.
You could rewrite your query as:
SELECT a.AddressID, p.ProcessID
FROM MMProcess AS p
CROSS JOIN MMAddress AS a
LEFT OUTER JOIN MMProcessItem AS i
ON i.AddressID = a.AddressID
AND i.ProcessID = p.ProcessID
WHERE i.ResultID IS NULL
ORDER BY p.ProcessID, a.AddressID;
Note the use of explicit join syntax, and also aliases for brevity
Since you are using the LEFT JOIN to MMProcessItem solely for the reason of removing records, then you might find that using NOT EXISTS conveys intention better, but more importantly, it can also perform better.
SELECT a.AddressID, p.ProcessID
FROM MMProcess AS p
CROSS JOIN MMAddress AS a
WHERE NOT EXISTS
( SELECT 1
FROM MMProcessItem AS i
WHERE i.AddressID = a.AddressID
AND i.ProcessID = p.ProcessID
)
ORDER BY p.ProcessID, a.AddressID;

SQL Server: Multiple table joins with a WHERE clause

I'm using SQL Server and I'm having a difficult time trying to get the results from a SELECT query that I want. I've tried joining in different orders and using subqueries but nothing quite works the way I want. Take this contrived example of software applications, with different version levels, that might be installed on peoples computers.
I need to perform a JOIN with a WHERE, but for some reason I can't get the results I want.
Maybe I'm looking at my data wrong, I'm not quite sure why I can't get this to work.
Application table
ID Name
1 Word
2 Excel
3 Powerpoint
Software Table (contains version information for different applications)
ID ApplicationID Version
1 1 2003
2 1 2007
3 2 2003
4 2 2007
5 3 2003
6 3 2007
Software_Computer junction table
ID SoftwareID ComputerID
1 1 1
2 4 1
3 2 2
4 5 2
Computer table
ID ComputerName
1 Name1
2 Name2
I want a query that I could run where I select a specific computer to display what software version and application is has, but I also want it to display what application it does not have(the version would be a NULL since it doesn't have that software on it)
SELECT Computer.ComputerName, Application.Name, Software.Version
FROM Computer
JOIN Software_Computer
ON Computer.ID = Software_Computer.ComputerID
JOIN Software
ON Software_Computer.SoftwareID = Software.ID
RIGHT JOIN Application
ON Application.ID = Software.ApplicationID
WHERE Computer.ID = 1
I want the following result set
ComputerName Name Version
Name1 Word 2003
Name1 Excel 2007
Name1 Powerpoint NULL
But I just get
Results
ComputerName Name Version
Name1 Word 2003
Name1 Excel 2007
I thought the RIGHT JOIN would include all the results in the application table, even if they aren't associated with the computer. What am I missing/doing wrong?
When using LEFT JOIN or RIGHT JOIN, it makes a difference whether you put the filter in the WHERE or into the JOIN.
See this answer to a similar question I wrote some time ago:
What is the difference in these two queries as getting two different result set?
In short:
if you put it into the WHERE clause (like you did, the results that aren't associated with that computer are completely filtered out
if you put it into the JOIN instead, the results that aren't associated with that computer appear in the query result, only with NULL values
--> this is what you want
The third row you expect (the one with Powerpoint) is filtered out by the Computer.ID = 1 condition (try running the query with the Computer.ID = 1 or Computer.ID is null it to see what happens).
However, dropping that condition would not make sense, because after all, we want the list for a given Computer.
The only solution I see is performing a UNION between your original query and a new query that retrieves the list of application that are not found on that Computer.
The query might look like this:
DECLARE #ComputerId int
SET #ComputerId = 1
-- your original query
SELECT Computer.ComputerName, Application.Name, Software.Version
FROM Computer
JOIN dbo.Software_Computer
ON Computer.ID = Software_Computer.ComputerID
JOIN dbo.Software
ON Software_Computer.SoftwareID = Software.ID
RIGHT JOIN dbo.Application
ON Application.ID = Software.ApplicationID
WHERE Computer.ID = #ComputerId
UNION
-- query that retrieves the applications not installed on the given computer
SELECT Computer.ComputerName, Application.Name, NULL as Version
FROM Computer, Application
WHERE Application.ID not in
(
SELECT s.ApplicationId
FROM Software_Computer sc
LEFT JOIN Software s on s.ID = sc.SoftwareId
WHERE sc.ComputerId = #ComputerId
)
AND Computer.id = #ComputerId
try this
DECLARE #Application TABLE(Id INT PRIMARY KEY, NAME VARCHAR(20))
INSERT #Application ( Id, NAME )
VALUES ( 1,'Word' ), ( 2,'Excel' ), ( 3,'PowerPoint' )
DECLARE #software TABLE(Id INT PRIMARY KEY, ApplicationId INT, Version INT)
INSERT #software ( Id, ApplicationId, Version )
VALUES ( 1,1, 2003 ), ( 2,1,2007 ), ( 3,2, 2003 ), ( 4,2,2007 ),( 5,3, 2003 ), ( 6,3,2007 )
DECLARE #Computer TABLE(Id INT PRIMARY KEY, NAME VARCHAR(20))
INSERT #Computer ( Id, NAME )
VALUES ( 1,'Name1' ), ( 2,'Name2' )
DECLARE #Software_Computer TABLE(Id INT PRIMARY KEY, SoftwareId int, ComputerId int)
INSERT #Software_Computer ( Id, SoftwareId, ComputerId )
VALUES ( 1,1, 1 ), ( 2,4,1 ), ( 3,2, 2 ), ( 4,5,2 )
SELECT Computer.Name ComputerName, Application.Name ApplicationName, MAX(Software2.Version) Version
FROM #Application Application
JOIN #Software Software
ON Application.ID = Software.ApplicationID
CROSS JOIN #Computer Computer
LEFT JOIN #Software_Computer Software_Computer
ON Software_Computer.ComputerId = Computer.Id AND Software_Computer.SoftwareId = Software.Id
LEFT JOIN #Software Software2
ON Software2.ID = Software_Computer.SoftwareID
WHERE Computer.ID = 1
GROUP BY Computer.Name, Application.Name
You need to do a LEFT JOIN.
SELECT Computer.ComputerName, Application.Name, Software.Version
FROM Computer
JOIN dbo.Software_Computer
ON Computer.ID = Software_Computer.ComputerID
LEFT JOIN dbo.Software
ON Software_Computer.SoftwareID = Software.ID
RIGHT JOIN dbo.Application
ON Application.ID = Software.ApplicationID
WHERE Computer.ID = 1
Here is the explanation:
The result of a left outer join (or simply left join) for table A and
B always contains all records of the "left" table (A), even if the
join-condition does not find any matching record in the "right" table
(B). This means that if the ON clause matches 0 (zero) records in B,
the join will still return a row in the result—but with NULL in each
column from B. This means that a left outer join returns all the
values from the left table, plus matched values from the right table
(or NULL in case of no matching join predicate). If the right table
returns one row and the left table returns more than one matching row
for it, the values in the right table will be repeated for each
distinct row on the left table. From Oracle 9i onwards the LEFT OUTER
JOIN statement can be used as well as (+).
SELECT p.Name, v.Name
FROM Production.Product p
JOIN Purchasing.ProductVendor pv
ON p.ProductID = pv.ProductID
JOIN Purchasing.Vendor v
ON pv.BusinessEntityID = v.BusinessEntityID
WHERE ProductSubcategoryID = 15
ORDER BY v.Name;
You almost have it. You need a Right join to the application, So it knows that the right table which is application is important
SELECT Computer.ComputerName, Application.Name, Software.Version
FROM Computer
JOIN Software_Computer
ON Computer.ID = Software_Computer.ComputerID
Right JOIN Software
ON Software_Computer.SoftwareID = Software.ID
RIGHT JOIN Application
ON Application.ID = Software.ApplicationID
WHERE Computer.ID = 1
Try this working fine....
SELECT computer.NAME, application.NAME,software.Version FROM computer LEFT JOIN software_computer ON(computer.ID = software_computer.ComputerID)
LEFT JOIN software ON(software_computer.SoftwareID = Software.ID) LEFT JOIN application ON(application.ID = software.ApplicationID)
where computer.id = 1 group by application.NAME UNION SELECT computer.NAME, application.NAME,
NULL as Version FROM computer, application WHERE application.ID not in ( SELECT s.applicationId FROM software_computer sc LEFT JOIN software s
on s.ID = sc.SoftwareId WHERE sc.ComputerId = 1 )
AND computer.id = 1
select C.ComputerName, S.Version, A.Name
from Computer C inner join Software_Computer SC
on C.Id = SC.ComputerId
Inner join Software S
on SC.SoftwareID = S.Id
Inner join Application A
on S.ApplicationId = A.Id ;
SELECT Computer.Computer_Name, Application1.Name, Max(Soft.[Version]) as Version1
FROM Application1
inner JOIN Software
ON Application1.ID = Software.Application_Id
cross join Computer
Left JOIN Software_Computer
ON Software_Computer.Computer_Id = Computer.ID and Software_Computer.Software_Id = Software.Id
Left JOIN Software as Soft
ON Soft.Id = Software_Computer.Software_Id
WHERE Computer.ID = 1
GROUP BY Computer.Computer_Name, Application1.Name

TSQL - Return recent date

Having issues getting a dataset to return with one date per client in the query.
Requirements:
Must have the recent date of transaction per client list for user
Will need have the capability to run through EXEC
Current Query:
SELECT
c.client_uno
, c.client_code
, c.client_name
, c.open_date
into #AttyClnt
from hbm_client c
join hbm_persnl p on c.resp_empl_uno = p.empl_uno
where p.login = #login
and c.status_code = 'C'
select
ba.payr_client_uno as client_uno
, max(ba.tran_date) as tran_date
from blt_bill_amt ba
left outer join #AttyClnt ac on ba.payr_client_uno = ac.client_uno
where ba.tran_type IN ('RA', 'CR')
group by ba.payr_client_uno
Currently, this query will produce at least 1 row per client with a date, the problem is that there are some clients that will have between 2 and 10 dates associated with them bloating the return table to about 30,000 row instead of an idealistic 246 rows or less.
When i try doing max(tran_uno) to get the most recent transaction number, i get the same result, some have 1 value and others have multiple values.
The bigger picture has 4 other queries being performed doing other parts, i have only included the parts that pertain to the question.
Edit (2011-10-14 # 1:45PM):
select
ba.payr_client_uno as client_uno
, max(ba.row_uno) as row_uno
into #Bills
from blt_bill_amt ba
inner join hbm_matter m on ba.matter_uno = m.matter_uno
inner join hbm_client c on m.client_uno = c.client_uno
inner join hbm_persnl p on c.resp_empl_uno = p.empl_uno
where p.login = #login
and c.status_code = 'C'
and ba.tran_type in ('CR', 'RA')
group by ba.payr_client_uno
order by ba.payr_client_uno
--Obtain list of Transaction Date and Amount for the Transaction
select
b.client_uno
, ba.tran_date
, ba.tc_total_amt
from blt_bill_amt ba
inner join #Bills b on ba.row_uno = b.row_uno
Not quite sure what was going on but seems the Temp Tables were not acting right at all. Ideally i would have 246 rows of data, but with the previous query syntax it would produce from 400-5000 rows of data, obviously duplications on data.
I think you can use ranking to achieve what you want:
WITH ranked AS (
SELECT
client_uno = ba.payr_client_uno,
ba.tran_date,
be.tc_total_amt,
rnk = ROW_NUMBER() OVER (
PARTITION BY ba.payr_client_uno
ORDER BY ba.tran_uno DESC
)
FROM blt_bill_amt ba
INNER JOIN hbm_matter m ON ba.matter_uno = m.matter_uno
INNER JOIN hbm_client c ON m.client_uno = c.client_uno
INNER JOIN hbm_persnl p ON c.resp_empl_uno = p.empl_uno
WHERE p.login = #login
AND c.status_code = 'C'
AND ba.tran_type IN ('CR', 'RA')
)
SELECT
client_uno,
tran_date,
tc_total_amt
FROM ranked
WHERE rnk = 1
ORDER BY client_uno
Useful reading:
Ranking Functions (Transact-SQL)
ROW_NUMBER (Transact-SQL)
WITH common_table_expression (Transact-SQL)
Using Common Table Expressions

Resources