Join with select SQL query? - sql

I'm trying to run a query on a table (the one Wordpress uses) where I want to select the ID and post_type columns from one table, then do a Left Join to another table, two separate times (getting separate data).
This is what I have so far, but it's not cutting the mustard:
SELECT derby_posts.id AS pID,
derby_posts.post_type AS tier
FROM derby_posts
LEFT JOIN (SELECT derby_postmeta.post_id AS dbID1,
derby_postmeta.meta_key AS dbMeta1)
ON pid = dbid1
AND dbmeta1 = 'twitter'
LEFT JOIN (SELECT derby_postmeta.post_id AS dbID2,
derby_postmeta.meta_key AS dbMeta2)
ON pid = dbid2
AND dbmeta2 = 'website'
WHERE tier IN ('local', 'regional', 'national')
I'm sure I'm missing something super simple...
Edit: here's the solution that worked for me. Table alias helped, putting all my SELECT statements together cleaned things up. Also, I realized I could remove items from the SELECT, even though I'm using them in the Join, which cleans up the results a lot.
SELECT
db.ID as id,
db.post_type as tier,
dpm1.meta_value as twitter,
dpm2.meta_value as website
FROM derby_posts db
LEFT JOIN derby_postmeta dpm1 ON (db.ID = dpm1.post_id AND dpm1.meta_key = 'twitter' )
LEFT JOIN derby_postmeta dpm2 ON (db.ID = dpm2.post_id AND dpm2.meta_key = 'website' )
WHERE db.post_type IN ('local','regional','national')

I 'm sure I'm missing something super simple...
You are right!
You need to give your selects an alias, and use that alias in the ON clause. You are also missing a FROM <table> - a required part of a SELECT statement that reads from a table:
LEFT JOIN (
SELECT derby_postmeta.post_id AS dbID1,
derby_postmeta.meta_key AS dbMeta1
FROM someTable
) dpm ON pid = dpm.dbid1 AND dpm.dbmeta1 = 'twitter'
I gave the results of your SELECT an alias dpm, and used it to "link up" the rows from the inner select to the rows of your outer select.

SELECT
db.derby_posts.ID as pID,
db.derby_posts.post_type as tier,
dpm1.post_id as dbID1,
dpm1.meta_key as dbMeta1,
dpm2.post_id as dbID2,
dpm2.meta_key as dbMeta2
FROM derby_posts db
LEFT JOIN derby_postmeta dpm1 ON (db.pID = dpm1.post_id AND dpm1.meta_key= 'twitter')
LEFT JOIN derby_postmeta dpm2 ON (db.pID = dbm2.post_id AND dbm2.meta_key = 'website')
WHERE tier IN ('local','regional','national')

Related

Basic SQL mechanics, create table, then call that table from another script

I have two scripts that work in isolation, but I don't know how to stitch them together to do this all at once.
The first script:
SELECT *
FROM PROD_ANALYTIC.SRC_MVC_AU_DBO.BCAPPLICATIONQUOTES bq
JOIN PROD_ANALYTIC.SRC_MVC_AU_DBO.PRODUCT p ON bq.ProductId = p.Id
JOIN PROD_ANALYTIC.SRC_MVC_AU_DBO.BCAPPLICATIONQUOTEFEES F ON F.ApplicationQuoteId = bq.Id
LEFT JOIN PROD_ANALYTIC.SRC_MVC_AU_DBO.COVERAGESECTIONTYPE CST ON F.CoverageSectionTypeID = CST.SECTIONID
This produces a table (is that the right word?). Suppose I could save that table as 'appQuote'. I then want to run this script on it:
SELECT
ApplicationId,
STRING_AGG(YEARLYPAYAMOUNT) allPremium,
STRING_AGG(SECTIONID) allSection,
STRING_AGG(SHORTNAME) allSectionName,
FROM
appQuote
GROUP BY
ApplicationId
For storage reasons I don't want to actually save the table from the first script, I just want to get the result from the first script and immediately apply the second script to it.
This is very basic, so any guidance would help.
The relevant term is known as a derived table.
In SQL you can use the result of a query as the input to another. The "nested query" produces the derived table which is then consumed by the outer query:
Select <columns>
from (
select <columns> from table join table etc
)as TableAliasName
To get the nomenclature right, what you have are not scripts or tables, but rather two queries. When a query runs it produces a result set.
To accomplish your goal, either of these queries will work:
SELECT
ApplicationId,
STRING_AGG(YEARLYPAYAMOUNT) allPremium,
STRING_AGG(SECTIONID) allSection,
STRING_AGG(SHORTNAME) allSectionName,
FROM (
SELECT *
FROM PROD_ANALYTIC.SRC_MVC_AU_DBO.BCAPPLICATIONQUOTES bq
JOIN PROD_ANALYTIC.SRC_MVC_AU_DBO.PRODUCT p ON bq.ProductId = p.Id
JOIN PROD_ANALYTIC.SRC_MVC_AU_DBO.BCAPPLICATIONQUOTEFEES F ON F.ApplicationQuoteId = bq.Id
LEFT JOIN PROD_ANALYTIC.SRC_MVC_AU_DBO.COVERAGESECTIONTYPE CST ON F.CoverageSectionTypeID = CST.SECTIONID
) appQuote
GROUP BY
ApplicationId
Here, the inner nested query is formally called a derived table, but people will often use the phrase "subquery".
The other option is a Common Table Expression (CTE):
With appQuote As (
SELECT *
FROM PROD_ANALYTIC.SRC_MVC_AU_DBO.BCAPPLICATIONQUOTES bq
JOIN PROD_ANALYTIC.SRC_MVC_AU_DBO.PRODUCT p ON bq.ProductId = p.Id
JOIN PROD_ANALYTIC.SRC_MVC_AU_DBO.BCAPPLICATIONQUOTEFEES F ON F.ApplicationQuoteId = bq.Id
LEFT JOIN PROD_ANALYTIC.SRC_MVC_AU_DBO.COVERAGESECTIONTYPE CST ON F.CoverageSectionTypeID = CST.SECTIONID
)
SELECT
ApplicationId,
STRING_AGG(YEARLYPAYAMOUNT) allPremium,
STRING_AGG(SECTIONID) allSection,
STRING_AGG(SHORTNAME) allSectionName,
FROM
appQuote
GROUP BY
ApplicationId
Finally, you could also create a View from the first query:
CREATE View AppQuote As
SELECT *
FROM PROD_ANALYTIC.SRC_MVC_AU_DBO.BCAPPLICATIONQUOTES bq
JOIN PROD_ANALYTIC.SRC_MVC_AU_DBO.PRODUCT p ON bq.ProductId = p.Id
JOIN PROD_ANALYTIC.SRC_MVC_AU_DBO.BCAPPLICATIONQUOTEFEES F ON F.ApplicationQuoteId = bq.Id
LEFT JOIN PROD_ANALYTIC.SRC_MVC_AU_DBO.COVERAGESECTIONTYPE CST ON F.CoverageSectionTypeID = CST.SECTIONID
A view is not just a query; it will actually become part of the database, such that you can use it in many of the same ways you would use a table saved on disk, and then the second query in the original question will run unmodified.

Can I do a left join without returning the conditional columns?

New to SQL but I want to be able to optimize my query by bringing just the right amount of data. I am doing a left join on CS Rep Name and WE, which are two columns present in both tables. I find that if I don't bring in CS Rep Name and WE in the TECDR table, the query would error. Is there a workaround to this? Since it is a left join, I don't need redundant data.
SELECT *
FROM Tish_Email_CSAT_Dump AS TECD
LEFT JOIN (SELECT CS_Rep_Name,
Team_Leader,
Operations_Manager,
Tenure,
WE,
FileName
FROM Tish_Email_CSAT_Dump_Roster) AS TECDR
ON TECD.CS_Rep_Name = TECDR.CS_Rep_Name
AND TECD.WE = TECDR.WE
When you embed a SELECT inside a query in place of a table, the result of a select (projection) behave like a table visible only inside the query.
In your case, the join is the same as if there were a table called TECDR with the columns that you select. Hence, if you leave out some columns of Tish_Email_CSAT_Dump_Roster from your SELECT, these columns would not be available for joining or selection.
However, in your case this is unnecessary: all you need to do is joining to the underlying table, like this:
SELECT
TECD.*
, TECDR.Team_Leader
, TECDR.Operations_Manager
, TECDR.Tenure
, TECDR.FileName
FROM Tish_Email_CSAT_Dump AS TECD
LEFT JOIN Tish_Email_CSAT_Dump_Roster AS TECDR
ON TECD.CS_Rep_Name = TECDR.CS_Rep_Name AND TECD.WE = TECDR.WE
select
<place the columns you want here>
from
Tish_Email_CSAT_Dump as TECD
Left join Tish_Email_CSAT_Dump_Roster as TECDR
On TECD.CS_Rep_Name = TECDR.CS_Rep_Name and TECD.WE = TECDR.WE
Hope the following helps or else please share the query that errors:
select TECD.Column1, TECD.Column2, TECDR.Column1, TECDR.Column2
from Tish_Email_CSAT_Dump as TECD
Left join Tish_Email_CSAT_Dump_Roster as TECDR
On TECD.CS_Rep_Name = TECDR.CS_Rep_Name and TECD.WE = TECDR.WE

Could not add the table (

The following code works in Sage200.
SELECT bcs.BomReference
,bcs.DateTimeCosted
,bcs.TotalCost
FROM (
SELECT BomReference
,Max(DateTimeCosted) AS MaxDate
FROM NDM_Sage200.dbo.BomCostSession BomCostSession
GROUP BY BomReference
) AS ldc
INNER JOIN BomCostSession AS bcs ON bcs.BomReference = ldc.BomReference
AND bcs.DateTimeCosted = ldc.MaxDate
ORDER BY BomReference
As soon as I try extending this with an INNER JOIN to another table to get more columns (using BomReference), I get the error message:
Could not add the table (.
See below for example of modified code; I have to use 2 joins to get to the table I need, but have the same error whatever I join onto the working code.
SELECT bcs.BomReference, bcs.DateTimeCosted, bcs.TotalCost, BomBuildProduct.StockDescription
FROM (
SELECT BomReference,
Max(DateTimeCosted) AS MaxDate
FROM NDM_Sage200.dbo.BomCostSession BomCostSession
GROUP BY BomReference
) AS ldc
INNER JOIN
BomCostSession as bcs
ON bcs.BomReference = ldc.BomReference AND
bcs.DateTimeCosted = ldc.MaxDate
***** Fails when adding INNER JOIN here *****
INNER JOIN
BomBuildPackage
ON BomCostSession.BomBuildPackageID = BomBuildPackage.BomBuildPackageID
INNER JOIN
BomBuildProduct
ON BomBuildPackage.BomRecordID = BomBuildProduct.BomRecordID
ORDER BY BomReference
What am I doing wrong ? I need to expand the query with data from several tables.
I also think that when using MSQuery on the section that works, it offers no options to add any tables - this makes it rather difficult to try options.
Why ?
The issue with MSQuery is that it attempts to display your query graphically in it's design view, this works OK for simple queries but not for complex queries which usually generates the could not add table message. The way I found around this is to treat your query as one big sub query inside a wrapper query, this forces MSQuery to give up the design view and work as pure SQL text.
Another issue might be that for one table you have the full path but not the others, is it correct for the table you have included it and does it need to be used on the other tables.
Here is an example of the changes I think you should make:
SELECT * FROM (
SELECT bcs.BomReference
,bcs.DateTimeCosted
,bcs.TotalCost
,BomBuildProduct.StockDescription
FROM
(SELECT BomReference
,Max(DateTimeCosted) AS MaxDate
FROM NDM_Sage200.dbo.BomCostSession BomCostSession
GROUP BY BomReference) AS ldc
INNER JOIN NDM_Sage200.dbo.BomCostSession AS bcs ON bcs.BomReference = ldc.BomReference
AND bcs.DateTimeCosted = ldc.MaxDate
INNER JOIN NDM_Sage200.dbo.BomBuildPackage ON BomCostSession.BomBuildPackageID = BomBuildPackage.BomBuildPackageID
INNER JOIN NDM_Sage200.dbo.BomBuildProduct ON BomBuildPackage.BomRecordID = BomBuildProduct.BomRecordID) x
ORDER BY BomReference

An Access problem: Not getting required output while building a report

I have created a report in Access and I have written a query for fetching records from
multiple tables as follows:
SELECT BuildingDetails.*, Contractors.Item, ActionDetails.ActionType
FROM Contractors
INNER JOIN (BuildingDetails
INNER JOIN (ActionDetails
INNER JOIN DormData ON ActionDetails.ActionID = DormData.ActionID)
ON BuildingDetails.BuildingID = DormData.BuildingID)
ON Contractors.ID = DormData.ItemID;
Now what I want is only actiontype=repair or actionid=1 get retrieved by the query. We have two actontype "repair" and "replace".
I have reformatted you query a little to neaten it up. You haven't specified what the data looks like for the filter but based on what you have said I would go with something like the following
SELECT BuildingDetails.*,
Contractors.Item,
ActionDetails.ActionType
FROM Contractors
INNER JOIN DormData ON Contractors.ID = DormData.ItemID
INNER JOIN ActionDetails ON DormData.ActionID = ActionDetails.ActionID
INNER JOIN BuildingDetails ON DormData.BuildingID = BuildingDetails.BuildingID
WHERE ActionDetails.ActionType = 'Repair' OR ActionID=1
If ActionID is a lookup column that relates ActionID(1) to ActionType ('Repair') then you don't need the or and can stick to one or other of the conditions in the WHERE Clause.
Hope this helps.
I suspect you only need to filter using actiontype = 'repair' (I further guess that ActionID is an autonumber and you have a row {ActionID = 1, actiontype = 'repair'} only by chance... but this is maybe extrapolating too far :)
I'm surprised #David Steele's answer works in Access (ACE, Jet, whatever) because he's removed the parentheses from the JOIN clauses (however if it does -- suggesting a linked table -- then you should "accept" that answer). But I too could resist 'neatening them up' so that the ON clauses are close to the table names:
SELECT BuildingDetails.*, Contractors.Item, ActionDetails.ActionType
FROM ((DormData
INNER JOIN Contractors
ON Contractors.ID = DormData.ItemID)
INNER JOIN BuildingDetails
ON BuildingDetails.BuildingID = DormData.BuildingID)
INNER JOIN ActionDetails
ON ActionDetails.ActionID = DormData.ActionID
WHERE ActionDetails.ActionType = 'repair';
Add this to the end of your select statement to fix the issue:
where actiondetails.actiontype = 'repair' or actiondetails.actionid = 1

Left Join With Where Clause

I need to retrieve all default settings from the settings table but also grab the character setting if exists for x character.
But this query is only retrieving those settings where character is = 1, not the default settings if the user havent setted anyone.
SELECT `settings`.*, `character_settings`.`value`
FROM (`settings`)
LEFT JOIN `character_settings`
ON `character_settings`.`setting_id` = `settings`.`id`
WHERE `character_settings`.`character_id` = '1'
So i should need something like this:
array(
'0' => array('somekey' => 'keyname', 'value' => 'thevalue'),
'1' => array('somekey2' => 'keyname2'),
'2' => array('somekey3' => 'keyname3')
)
Where key 1 and 2 are the default values when key 0 contains the default value with the character value.
The where clause is filtering away rows where the left join doesn't succeed. Move it to the join:
SELECT `settings`.*, `character_settings`.`value`
FROM `settings`
LEFT JOIN
`character_settings`
ON `character_settings`.`setting_id` = `settings`.`id`
AND `character_settings`.`character_id` = '1'
When making OUTER JOINs (ANSI-89 or ANSI-92), filtration location matters because criteria specified in the ON clause is applied before the JOIN is made. Criteria against an OUTER JOINed table provided in the WHERE clause is applied after the JOIN is made. This can produce very different result sets. In comparison, it doesn't matter for INNER JOINs if the criteria is provided in the ON or WHERE clauses -- the result will be the same.
SELECT s.*,
cs.`value`
FROM SETTINGS s
LEFT JOIN CHARACTER_SETTINGS cs ON cs.setting_id = s.id
AND cs.character_id = 1
If I understand your question correctly you want records from the settings database if they don't have a join accross to the character_settings table or if that joined record has character_id = 1.
You should therefore do
SELECT `settings`.*, `character_settings`.`value`
FROM (`settings`)
LEFT OUTER JOIN `character_settings`
ON `character_settings`.`setting_id` = `settings`.`id`
WHERE `character_settings`.`character_id` = '1' OR
`character_settings`.character_id is NULL
You might find it easier to understand by using a simple subquery
SELECT `settings`.*, (
SELECT `value` FROM `character_settings`
WHERE `character_settings`.`setting_id` = `settings`.`id`
AND `character_settings`.`character_id` = '1') AS cv_value
FROM `settings`
The subquery is allowed to return null, so you don't have to worry about JOIN/WHERE in the main query.
Sometimes, this works faster in MySQL, but compare it against the LEFT JOIN form to see what works best for you.
SELECT s.*, c.value
FROM settings s
LEFT JOIN character_settings c ON c.setting_id = s.id AND c.character_id = '1'
For this problem, as for many others involving non-trivial left joins such as left-joining on inner-joined tables, I find it convenient and somewhat more readable to split the query with a with clause. In your example,
with settings_for_char as (
select setting_id, value from character_settings where character_id = 1
)
select
settings.*,
settings_for_char.value
from
settings
left join settings_for_char on settings_for_char.setting_id = settings.id;
The way I finally understand the top answer is realising (following the Order Of Execution of the SQL query ) that the WHERE clause is applied to the joined table thereby filtering out rows that do not satisfy the WHERE condition from the joined (or output) table. However, moving the WHERE condition to the ON clause applies it to the individual tables prior to joining. This enables the left join to retain rows from the left table even though some column entries of those rows (entries from the right tables) do not satisfy the WHERE condition.
The result is correct based on the SQL statement. Left join returns all values from the right table, and only matching values from the left table.
ID and NAME columns are from the right side table, so are returned.
Score is from the left table, and 30 is returned, as this value relates to Name "Flow". The other Names are NULL as they do not relate to Name "Flow".
The below would return the result you were expecting:
SELECT a.*, b.Score
FROM #Table1 a
LEFT JOIN #Table2 b
ON a.ID = b.T1_ID
WHERE 1=1
AND a.Name = 'Flow'
The SQL applies a filter on the right hand table.