Merge / Join SQL Select Queries - sql

I am struggling with combining the below Select Statments, I know I could cheat and add some fake columns in and then use Union, but I want to do this correctly.
Once I have them joined, I will be putting the Statment in to a XML file for use with Word and CRM4.
SELECT BILLTO_NAME,
BILLTO_LINE1,
BILLTO_LINE2,
BILLTO_LINE3,
BILLTO_CITY,
BILLTO_COUNTRY,
BILLTO_POSTALCODE,
ORDERNUMBER,
REQUESTDELIVERYBY,
MODIFIEDON,
SHIPTO_NAME,
SHIPTO_LINE1,
SHIPTO_LINE2,
SHIPTO_LINE3,
SHIPTO_CITY,
SHIPTO_STATEORPROVINCE,
SHIPTO_COUNTRY,
SHIPTO_POSTALCODE,
CREATEDBY
FROM SALESORDERBASE
SELECT QUANTITY,
DESCRIPTION
FROM SALESORDERDETAILBASE
SELECT NEW_ORDERNOTES,
NEW_NOTES
FROM SALESORDEREXTENSIONBASE
They all have the common column of SalesOrderID, which I need to add in somewhere as well.

You can use a LEFT JOIN on the tables:
SELECT ob.SalesOrderID
ob.BILLTO_NAME,
ob.BILLTO_LINE1,
ob.BILLTO_LINE2,
ob.BILLTO_LINE3,
ob.BILLTO_CITY,
ob.BILLTO_COUNTRY,
ob.BILLTO_POSTALCODE,
ob.ORDERNUMBER,
ob.REQUESTDELIVERYBY,
ob.MODIFIEDON,
ob.SHIPTO_NAME,
ob.SHIPTO_LINE1,
ob.SHIPTO_LINE2,
ob.SHIPTO_LINE3,
ob.SHIPTO_CITY,
ob.SHIPTO_STATEORPROVINCE,
ob.SHIPTO_COUNTRY,
ob.SHIPTO_POSTALCODE,
ob.CREATEDBY,
od.QUANTITY,
od.DESCRIPTION,
oe.NEW_ORDERNOTES,
oe.NEW_NOTES
FROM SALESORDERBASE ob
LEFT JOIN SALESORDERDETAILBASE od
on ob.SalesOrderID = od.SalesOrderID
LEFT JOIN SALESORDEREXTENSIONBASE oe
on ob.SalesOrderID = oe.SalesOrderID

Assuming the column that identifies the relationship is called id on all three tables, you can do this:
SELECT sob.BILLTO_NAME,
sob.BILLTO_LINE1,
sob.BILLTO_LINE2,
sob.BILLTO_LINE3,
sob.BILLTO_CITY,
sob.BILLTO_COUNTRY,
sob.BILLTO_POSTALCODE,
sob.ORDERNUMBER,
sob.REQUESTDELIVERYBY,
sob.MODIFIEDON,
sob.SHIPTO_NAME,
sob.SHIPTO_LINE1,
sob.SHIPTO_LINE2,
sob.SHIPTO_LINE3,
sob.SHIPTO_CITY,
sob.SHIPTO_STATEORPROVINCE,
sob.SHIPTO_COUNTRY,
sob.SHIPTO_POSTALCODE,
sob.CREATEDBY,
sodb.QUANTITY,
sodb.DESCRIPTION,
soeb.NEW_ORDERNOTES,
soeb.NEW_NOTES
From SalesOrderBase sob
JOIN SalesOrderDetailBase sodb
ON sob.id = sodb.SalesOrderID
JOIN SalesOrderExtensionBase soeb
ON sob.id = soeb.SalesOrderID
You can think of JOINing as slamming together rows side-by-side, whereas UNIONing is slamming together rows one on top of the other. UNIONS require that the columns be the same and JOINs require that there is a relationship of some kind between each row.
EDIT - The OP provided more details

Related

CONCAT value in ON condition of JOIN

I have two tables.
Table: Geonames:
Country (2 character ISO code, e.g. SE)
AdminArea (Char code, e.g. 0330)
Table AdminAreas
AdminCode, (Combination of Coutry and AdminArea, e.g. "SE.0330")
So the ID of the AdminAreas that I want to join the tables on is a combination of columns on the first table. To join it I will need to join the two values from the Geonames table. Something like.
SELECT
geoname.geonameid, geoname.name, geoname.latitude, geoname.longitude,
geoname.country, geoname.admin1, admin_one.admin_id, admin_one.geoname_id
FROM geoname
INNER JOIN admin_one ON admin_one.admin_id = CONCAT(geoname.country, '.', geoname.admin1)
WHERE country='SE' LIMIT 10
Unfortunately, this is not working. It does not seem like i can CONCAT or do string_agg() on a JOIN. How do I get this JOIN working?
Your code is fine. Perhaps one issue is that you have spaces or some other character. I would recommend investigating this using:
SELECT gn.geonameid, gn.name, gn.latitude, gn.longitude,
gn.country, gn.admin1, ao.admin_id, ao.geoname_id
FROM geoname gn LEFT JOIN
admin_one ao
ON ao.admin_id = CONCAT(TRIM(gn.country), '.', TRIM(gn.admin1))
WHERE gn.country = 'SE'
LIMIT 10;
This will return even unmatched results (because of the LEFT JOIN). That might help you investigate the issue.

SQL SERVER 2008 - joins

I have three tables that I'm trying to join to get the necessary data. Here they are...
*TblComp* *TblCompParent* *tblCompProcesses*
CompID CompBillingID CompID
CompBillingID Capacity1 CompProcessID
Capacity2
So what I'm trying to do with these three tables is....
Select tblCompParent.Capacity1, tblCompParent.Capacity2, CompProcessID
My problem is this...In tblComp there are 351 values - so i start off by joining tblComp and tblCompparent and SELECTING tblCompparent.capacity1, tblcompparent.capacity2, the query looks like this...
SELECT dbo.tblComp.CompID, dbo.tblCompParents.Capacity1, dbo.tblCompParents.Capacity2
FROM dbo.tblCompParents INNER JOIN
dbo.tblComp ON dbo.tblCompParents.CompBillingID = dbo.tblComp.CompBillingID
And this works fine, it's when I try to join tblCompProcess to pull the CompProcessID is when I get like 580 records. I'm not sure what can of join I have to do on tblCompprocess to select only one CompProcessID per compID.
And it seems like i have to use tblComp otherwise I'll have no way of joining tblCompProcess.
EDIT1:
SELECT dbo.tblComp.CompID, dbo.tblCompParents.Capacity1, dbo.tblCompParents.Capacity2, tblCompProcess.compprocessID
FROM dbo.tblCompParents INNER JOIN
dbo.tblComp ON dbo.tblCompParents.CompBillingID = dbo.tblComp.CompBillingID
Inner Join dbo.tblCompprocess on tblCompProcess.CompID = tblComp.CompID
You don't show us the whole data model so I don't know exactly what is going on but clearly tblCompProcesses has more than one row in your join. I would fix it like this:
SELECT dbo.tblComp.CompID, dbo.tblCompParents.Capacity1, dbo.tblCompParents.Capacity2, x.compprocessID
FROM dbo.tblCompParents
INNER JOIN dbo.tblComp ON dbo.tblCompParents.CompBillingID = dbo.tblComp.CompBillingID
INNER JOIN (SELECT DISTINCT CompID, compprocessID
FROM dbo.tblCompprocess) X on x.CompID = tblComp.CompID

Include missing years in Group By query

I am fairly new in Access and SQL programming. I am trying to do the following:
Sum(SO_SalesOrderPaymentHistoryLineT.Amount) AS [Sum Of PaymentPerYear]
and group by year even when there is no amount in some of the years. I would like to have these years listed as well for a report with charts. I'm not certain if this is possible, but every bit of help is appreciated.
My code so far is as follows:
SELECT
Base_CustomerT.SalesRep,
SO_SalesOrderT.CustomerId,
Base_CustomerT.Customer,
SO_SalesOrderPaymentHistoryLineT.DatePaid,
Sum(SO_SalesOrderPaymentHistoryLineT.Amount) AS [Sum Of PaymentPerYear]
FROM
Base_CustomerT
INNER JOIN (
SO_SalesOrderPaymentHistoryLineT
INNER JOIN SO_SalesOrderT
ON SO_SalesOrderPaymentHistoryLineT.SalesOrderId = SO_SalesOrderT.SalesOrderId
) ON Base_CustomerT.CustomerId = SO_SalesOrderT.CustomerId
GROUP BY
Base_CustomerT.SalesRep,
SO_SalesOrderT.CustomerId,
Base_CustomerT.Customer,
SO_SalesOrderPaymentHistoryLineT.DatePaid,
SO_SalesOrderPaymentHistoryLineT.PaymentType,
Base_CustomerT.IsActive
HAVING
(((SO_SalesOrderPaymentHistoryLineT.PaymentType)=1)
AND ((Base_CustomerT.IsActive)=Yes))
ORDER BY
Base_CustomerT.SalesRep,
Base_CustomerT.Customer;
You need another table with all years listed -- you can create this on the fly or have one in the db... join from that. So if you had a table called alltheyears with a column called y that just listed the years then you could use code like this:
WITH minmax as
(
select min(year(SO_SalesOrderPaymentHistoryLineT.DatePaid) as minyear,
max(year(SO_SalesOrderPaymentHistoryLineT.DatePaid) as maxyear)
from SalesOrderPaymentHistoryLineT
), yearsused as
(
select y
from alltheyears, minmax
where alltheyears.y >= minyear and alltheyears.y <= maxyear
)
select *
from yearsused
join ( -- your query above goes here! -- ) T
ON year(T.SO_SalesOrderPaymentHistoryLineT.DatePaid) = yearsused.y
You need a data source that will provide the year numbers. You cannot manufacture them out of thin air. Supposing you had a table Interesting_year with a single column year, populated, say, with every distinct integer between 2000 and 2050, you could do something like this:
SELECT
base.SalesRep,
base.CustomerId,
base.Customer,
base.year,
Sum(NZ(data.Amount)) AS [Sum Of PaymentPerYear]
FROM
(SELECT * FROM Base_CustomerT INNER JOIN Year) AS base
LEFT JOIN
(SELECT * FROM
SO_SalesOrderT
INNER JOIN SO_SalesOrderPaymentHistoryLineT
ON (SO_SalesOrderPaymentHistoryLineT.SalesOrderId = SO_SalesOrderT.SalesOrderId)
) AS data
ON ((base.CustomerId = data.CustomerId)
AND (base.year = Year(data.DatePaid))),
WHERE
(data.PaymentType = 1)
AND (base.IsActive = Yes)
AND (base.year BETWEEN
(SELECT Min(year(DatePaid) FROM SO_SalesOrderPaymentHistoryLineT)
AND (SELECT Max(year(DatePaid) FROM SO_SalesOrderPaymentHistoryLineT))
GROUP BY
base.SalesRep,
base.CustomerId,
base.Customer,
base.year,
ORDER BY
base.SalesRep,
base.Customer;
Note the following:
The revised query first forms the Cartesian product of BaseCustomerT with Interesting_year in order to have base customer data associated with each year (this is sometimes called a CROSS JOIN, but it's the same thing as an INNER JOIN with no join predicate, which is what Access requires)
In order to have result rows for years with no payments, you must perform an outer join (in this case a LEFT JOIN). Where a (base customer, year) combination has no associated orders, the rest of the columns of the join result will be NULL.
I'm selecting the CustomerId from Base_CustomerT because you would sometimes get a NULL if you selected from SO_SalesOrderT as in the starting query
I'm using the Access Nz() function to convert NULL payment amounts to 0 (from rows corresponding to years with no payments)
I converted your HAVING clause to a WHERE clause. That's semantically equivalent in this particular case, and it will be more efficient because the WHERE filter is applied before groups are formed, and because it allows some columns to be omitted from the GROUP BY clause.
Following Hogan's example, I filter out data for years outside the overall range covered by your data. Alternatively, you could achieve the same effect without that filter condition and its subqueries by ensuring that table Intersting_year contains only the year numbers for which you want results.
Update: modified the query to a different, but logically equivalent "something like this" that I hope Access will like better. Aside from adding a bunch of parentheses, the main difference is making both the left and the right operand of the LEFT JOIN into a subquery. That's consistent with the consensus recommendation for resolving Access "ambiguous outer join" errors.
Thank you John for your help. I found a solution which works for me. It looks quiet different but I learned a lot out of it. If you are interested here is how it looks now.
SELECT DISTINCTROW
Base_Customer_RevenueYearQ.SalesRep,
Base_Customer_RevenueYearQ.CustomerId,
Base_Customer_RevenueYearQ.Customer,
Base_Customer_RevenueYearQ.RevenueYear,
CustomerPaymentPerYearQ.[Sum Of PaymentPerYear]
FROM
Base_Customer_RevenueYearQ
LEFT JOIN CustomerPaymentPerYearQ
ON (Base_Customer_RevenueYearQ.RevenueYear = CustomerPaymentPerYearQ.[RevenueYear])
AND (Base_Customer_RevenueYearQ.CustomerId = CustomerPaymentPerYearQ.CustomerId)
GROUP BY
Base_Customer_RevenueYearQ.SalesRep,
Base_Customer_RevenueYearQ.CustomerId,
Base_Customer_RevenueYearQ.Customer,
Base_Customer_RevenueYearQ.RevenueYear,
CustomerPaymentPerYearQ.[Sum Of PaymentPerYear]
;

Access Left Join not working properly

In access I wrote this query:
Select
I.sysid, I.MemberNumber, I.Date, I.Distributer,
F.MemberNumber as FMember, F.Date as FDate, I.Distributer as FDistributer
From Initial as I
Left Join Final as F ON
I.MemberNumber=F.MemberNumber and
I.Distributer=F.Distributer and
I.Date>=F.Date-14 and
I.Date<=F.Date+14;
But the left join is not behavior properly. There are fewer rows in this table then there are in Initial... but it should be keeping ALL rows from initial, because I am using a left join, right? I have found several rows in initial (like sysid=7, which is Initial's key) that just isn't coming into this table.
It may have to do with your AND logic. Add some ( ) parenthesis to this to include it all like so:
Select
I.sysid, I.MemberNumber, I.Date, I.Distributer,
F.MemberNumber as FMember, F.Date as FDate, I.Distributer as FDistributer
From Initial as I
Left Join Final as F ON
(I.MemberNumber=F.MemberNumber and
I.Distributer=F.Distributer and
(I.Date>=F.Date-14) and
(I.Date<=F.Date+14));
Also I think there is a dateadd function, I'd use that instead of + / -.
If you're in the Query Designer, make sure all the filters are cleared. I've built your tables and sql and can't reproduce your error.
SELECT I.sysid
, I.MemberNumber
, I.Dated
, I.Distributer
, F.MemberNumber
, F.Dated AS FDated
, F.Distributer AS FDistributer
FROM Initial AS I
LEFT JOIN Final AS F
ON I.Distributer = F.Distributer
AND I.MemberNumber = F.MemberNumber
AND I.Dated>=F.Dated-14
AND I.Dated<=F.Dated+14;
Try adding additional fields to the original table and using update queries to add extra columns. This way you can be sure you won't drop any columns.

Why do I have to use DISTINCT for this to work?

here's my problem: I have an SQL query that makes 4 calls to a lookup table to return their values from a list of combinations in another table. I finally got this working, and for some reason, when I run the query without DISTINCT, I get a ton of data back, so I'm guessing that I'm either missing something or not doing this correctly. It would be really great if this would not only work, but also return the list alphabetically by the first colour name.
I'm putting my SQL here I hope I've explained this well enough:
SELECT DISTINCT
colour1.ColourID AS colour1_ColourID,
colour1.ColourName AS colour1_ColourName,
colour1.ColourHex AS colour1_ColourHex,
colour1.ManufacturerColourID AS colour1_ManufacturerColourID,
colour2.ColourID AS colour2_ColourID,
colour2.ColourName AS colour2_ColourName,
colour2.ColourHex AS colour2_ColourHex,
colour2.QEColourID2 AS colour2_QEColourID2,
colour3.ColourID AS colour3_ColourID,
colour3.ColourName AS colour3_ColourName,
colour3.ColourHex AS colour3_ColourHex,
colour3.QEColourID3 AS colour3_QEColourID3,
colour4.ColourID AS colour4_ColourID,
colour4.ColourName AS colour4_ColourName,
colour4.ColourHex AS colour4_ColourHex,
colour4.QEColourID4 AS colour4_QEColourID4,
Combinations.ID,
Combinations.ManufacturerColourID AS Combinations_ManufacturerColourID,
Combinations.QEColourID2 AS Combinations_QEColourID2,
Combinations.QEColourID3 AS Combinations_QEColourID3,
Combinations.QEColourID4 AS Combinations_QEColourID4,
Combinations.ColourSupplierID,
ColourSuppliers.ColourSupplier
FROM
ColourSuppliers INNER JOIN
(
colour4 INNER JOIN
(
colour3 INNER JOIN
(
colour2 INNER JOIN
(
colour1 INNER JOIN Combinations ON
colour1.ColourID=Combinations.ManufacturerColourID
) ON colour2.ColourID=Combinations.QEColourID2
) ON colour3.ColourID=Combinations.QEColourID3
) ON colour4.ColourID=Combinations.QEColourID4
) ON ColourSuppliers.ColourSupplierID=Combinations.ColourSupplierID
WHERE Combinations.ColourSupplierID = ?
Thanks
Steph
It looks as though you've probably got multiple records for each set of four colour combinations in the Combinations table - posting the structure of the table might help us to work it out.
Adding the clause order by colour1.ColourName to the end of the query should sort it alphabetically by the first colour name.
My guess (and it is a guess because your SQL query is very wide!) is that you're getting the cartesian product.