I am trying to perform a SELECT query on two tables joined with a LEFT JOIN where there may not be a record in the joined table. Something like:
--SELECT row using AreaID
SELECT *
FROM Rate
LEFT JOIN Area
ON Rate.AreaID = Area.AreaID
WHERE ProductID = #ProductID
AND Area.PostcodeOutcode = #PostcodeOutcode
This works when #PostcodeOutcode exists in the Area table, but I still need to return the record in the left table if there is not a record in the right table.
I am fudging it currently by doing this, but I know there is a better solution:
DECLARE #AreaID int
SELECT #AreaID = AreaID
FROM Area WHERE PostcodeOutcode = #PostcodeOutcode
--SELECT row using AreaID
SELECT *
FROM Rate
WHERE ProductID = #ProductID
AND
(
AreaID = #AreaID
OR (#AreaID IS NULL AND AreaID IS NULL)
)
I know this is probably simple, but my SQL knowledge is limited. Please help.
Thanks
Alex
move the area check to the join
SELECT * FROM Rate
LEFT JOIN Area
ON Rate.AreaID = Area.AreaID and Area.PostcodeOutcode = #PostcodeOutcode
WHERE ProductID = #ProductID
Update for the revised question in comments, Is this what you want?
SELECT Rate.RatePercent FROM Rate
INNER JOIN Area
ON Rate.AreaID = Area.AreaID and Area.PostcodeOutcode = #PostcodeOutcode
WHERE
ProductID = #ProductID
UNION ALL
SELECT Rate.RatePercent FROM Rate
where
ProductID = #ProductID
and
AreaId is null
and
not exists(select PostCodeOutCode From Area where PostCodeOutCode=#PostCodeOutcode)
There is a difference for left join between these two:
Select *
From Table1 t1
Left Outer Join Table2 t2 On t2.id = t1.id
Where t2.somevalue = #SomeParameter
And
Select *
From dbo.Table1 t1
Left Outer Join dbo.Table2 t2 On t2.id = t1.id And t2.somevalue = #SomeParameter
The latter will filter Table2, while the former will filter the join between Table1 and Table2. So, this means that the first query will join all rows in the two tables on id and then filter the ones where somevalue doesn't match the parameter, i.e. this will usually also filter out the ones where somevalue is null, because there was no row.
The second query will join table1 with table2 on id, but table2 is filtered on matching parameter first, so the non-matching rows are also returned, and are thus not filtered out.
And a side note: you should always supply the schema of your tables in your queries (for performance reasons).
Related
I have 2 tables that showing data Item master and BOM. I would like to join the tables between Item master as T1 and BOM as T2 and the additional table for table BOM as T3. Item master table containing ITM_CD, ITM_TYP (1,2,3,4) where each ITM_TYP represents a code for the first digit on ITM_CD. The thing that I want is like the picture below
CHILD_CD2 value replace to CHILD_CD1 value. So the data should be like this. What query should I fix ? I am very new using oracle query.
Here is mycode;
SELECT DISTINCT
T1.ITM_CD,
T2.C_ITM_CD AS CHILD_CD1,
T3.C_ITM_CD AS CHILD_CD2
FROM CM_HINMO_ALL T1
INNER JOIN (SELECT P_ITM_CD, C_ITM_CD, BOM_PTN FROM SM_BOM_ALL) T2
ON T1.ITM_CD = T2.P_ITM_CD
LEFT JOIN (SELECT P_ITM_CD, C_ITM_CD, BOM_PTN FROM SM_BOM_ALL) T3
ON T2.C_ITM_CD = t3.P_ITM_CD
WHERE 0=0
AND T2.BOM_PTN IN (1)
AND T1.ITM_TYP IN (1,2)
AND T1.ITM_CD = '110100370'
ORDER BY 2
Just use Case expression to replace the values.
SELECT ITM_CD, CASE WHEN CHILD_CD2 IS NULL THEN CHILD_CD2 ELSE CHILD_CD1 END AS CHILD_CD1
FROM TABLE1
If I understood, you want child_cd2 value should taken precedence over child_cd1 if available. If this assumption is right then we can use coalesce which returns the fist non null expression to achieve the same.
SELECT DISTINCT
T1.ITM_CD,
COALESCE(T3.C_ITM_CD,T2.C_ITM_CD) AS CHILD_CD1
FROM CM_HINMO_ALL T1
INNER JOIN SM_BOM_ALL T2
ON T1.ITM_CD = T2.P_ITM_CD
LEFT JOIN SM_BOM_ALL T3
ON T2.C_ITM_CD = t3.P_ITM_CD
WHERE T2.BOM_PTN IN (1)
AND T1.ITM_TYP IN (1,2)
AND T1.ITM_CD = '110100370'
ORDER BY 2
When I run the query below :
SELECT COUNT(x.objectID)
FROM db0..table0 as t
INNER JOIN db1..table1 as x ON t.objID = x.slaveID
INNER JOIN db1..table2 as table2 ON table2.sourceID = x.objectID
WHERE (****)
I get 268'466 results. However when I update and add a column to db0..table0 with x.objectID as follows, I get 145'346 of these items into my db0.table0
ALTER TABLE db0..table0 ADD new_objID bigint;
UPDATE db0..table0
SET db0..table0.new_objID = x.objectID
FROM db0..table0 as t
INNER JOIN db1..table1 as x ON t.objID = x.slaveID
INNER JOIN db1..table2 as table2 ON table2.sourceID = x.objectID
WHERE (****)
Can anyone see what is going wrong? The only difference between the queries is the first line in the first query is replaced with the first two lines in the second query.
To count the number of new values that end up in my table I use,
SELECT COUNT(new_objID)
FROM db0..table0
This should return all the none NULL instances of new_objID.
EDIT
So the table structures are
table0
table0_ID
table1
table1_ID
other_table1_ID
value
table0 and table1 are linked by table0_ID and table1_ID in a many to one relationship. One table0_ID corresponds to many table1_ID. I realised that table2 was no longer necessary - in the past I wanted information from this table but not any longer.
Effectively all I am trying to do is add the other_table1_ID entry, which corresponds to the smallest entry of value for each group of table1_ID into table0.
The issue is the discrepancy between these queries suggest I am doing something wrong I just can't work out what.
QUERY ONE
SELECT COUNT(table1.table1_ID)
FROM db0..table0 as table0
INNER JOIN db1..table1 as table1
ON table0.table0_ID = table1.table1_ID
WHERE table1.value IN (SELECT MIN(value)
FROM db1..table1 as new_table1
WHERE new_table1.table1_ID = table1.table1_ID)
QUERY TWO
ALTER TABLE db0..table0 ADD newID bigint
UPDATE db0..table0
SET db0..table0.newID = table1.other_table1_ID
FROM db0..table0 as table0
INNER JOIN db1..table1 as table1
ON table0.table0_ID = table1.table1_ID
WHERE table1.value IN (SELECT MIN(value)
FROM db1..table1 as new_table1
WHERE new_table1.table1_ID = table1.table1_ID)
UPDATE: after some discussion and question update by OP we came to the conclusion that conditions in both queries should be changed to the following:
new_table1.table1_ID = table1.table1_ID should instead be table0.table0_ID = new_table1.table1_ID
then both SELECT queries (original and the one which counts the newID field) return same count of 206146 records.
In the first query you do COUNT(x.objectID) but in the UPDATE call you SET db0..table0.new_objID = x.objID .
Notice, different column names: x.objectID in the first case and x.objID in the second.
Change your second query to the following:
UPDATE db0..table0
SET db0..table0.new_objID = x.objectID
FROM db0..table0 as t
INNER JOIN db1..table1 as x
ON t.objID = x.slaveID
INNER JOIN db1..table2 as table2
ON table2.sourceID = x.objectID
WHERE (****)
I have a join query I use to pull data from another table:
SELECT [THEME].[NAME],
[THEMETYPE].[TYPE]
FROM [THEME]
LEFT OUTER JOIN [THEMETYPE]
ON [THEME].[THEMETYPEID] = [THEMETYPE].[PK_THEMETYPE]
WHERE COALESCE([THEME].[THEMETYPEID], 'null') LIKE '%'
ORDER BY CASE
WHEN [THEMETYPE].[TYPE] IS NULL THEN 1
ELSE 0
END,
[THEMETYPE].[TYPE]
I need to add the ability to narrow it down if a 3rd tables values match up:
Where producttheme.productid = variable-paramater-here
AND producttheme.themeid = theme.pk_theme
Here is a pic of the table:
So if the 1 is chosen above, it will return all [Theme].[Name] and the associated [ThemeType].[Type] where The ThemeId is associated with ProductId = 1
Edit: to be more clear ThemeId is the Primary key in the Theme table where Theme.Name exists.
This would give you some idea, please adjust the column names accordingly:
SELECT [Theme].[Name], [ThemeType].[Type]
FROM [Theme]
Left Outer Join [ThemeType]
ON [Theme].[ThemeTypeId] = [ThemeType].[PK_ThemeType]
join ProductTheme PT
on PT.ProductID=ThemeType.ProductID
WHERE ProductTheme.ProductID = VARIABLE-PARAMATER-HERE AND ProductTheme.ThemeId = Theme.PK_Theme
ORDER BY [ThemeType].[Type]
Depending on whether or not you need the WHERE condition before you add the 3rd table, you can try one of these 2 options:
SELECT *
FROM TABLE1 T1
LEFT OUTER JOIN TABLE2 T2
ON T1.FIELDA = T2.FIELDA
INNER JOIN TABLE3 T3
ON T1.FIELDA = T3.FIELDA
WHERE T1.FIELDB = 'aaa'
AND T3.FIELDC = 12
or:
SELECT *
FROM (SELECT T1.FIELDA,
T2.FIELDB
FROM TABLE1 T1
LEFT OUTER JOIN TABLE2 T2
ON T1.FIELDA = T2.FIELDA
WHERE T1.FIELDC = 'aaa')T3
INNER JOIN TABLE3 T4
ON T3.FIELDA = T3.FIELDA
AND T4.FIELDC = 12
I hope this gives you something to work with.
If you provide some sample data, I can set up a working example.
I have following table which I want to update using another table, given below.alt text http://img94.imageshack.us/img94/4602/leisureoriginal.png
I want to update Null values of above given table using following table on the basis of ProductId.
alt text http://img264.imageshack.us/img264/512/datatable2.png
The updated table should be like this.
alt text http://img690.imageshack.us/img690/9585/updatedtable.png
I have mentioned ProductId in these table just for example. I don't know exact ProductId. It could be any ProductId.
I know FieldId and FieldValue in advance in 2nd table.
Can I do this in one UPDATE statement for all columns.
In SQL Server, the PIVOT keyword turns rows into columns. We need two PIVOTs, one for FieldId and one for FieldValue. The ;WITH keyword (which is preceded by a semicolon to distinguish it from the unrelated WITH ROLLUP command) allows us to use create "temporary views" which we use later in the UPDATE statement.
;WITH FieldIds AS (SELECT * FROM (SELECT ProductId, FieldId FROM ProductFields) A
PIVOT (MAX(FieldId) FOR FieldId IN ([50], [55], [60])) AS B),
FieldValues AS (SELECT * FROM ProductFields
PIVOT (MAX(FieldValue) FOR FieldId IN ([50], [55], [60])) AS C)
UPDATE Products
SET
RatingId = FieldIds.[50],
Rating = FieldValues.[50],
LeisureId = FieldIds.[55],
Leisure = FieldValues.[55],
SpaId = FieldIds.[60],
Spa = FieldValues.[60]
FROM Products
INNER JOIN FieldIds ON FieldIds.ProductId = Products.ProductId
INNER JOIN FieldValues ON FieldValues.ProductId = Products.ProductId
In SQL Server, an UPDATE statement allows a FROM clause with JOINS. For example, this query would update the Rating field:
UPDATE p
SET p.Rating = pf.FieldValue
FROM Products p
INNER JOIN ProductField pf
ON pf.ProductId = p.ProductId
WHERE pf.FieldId = 50
You could copy this query for the other fields. It's also possible to update more fields in query, but that seems unnecessary in this case.
You will first need to transform your second table so that it contains only one row per ProductID.
SELECT t1.ProductID, t1.FieldID AS RatingID, t1.FieldValue AS Rating,
t2.FieldID AS LeisureID, t2.FieldValue AS Leisure, etc.
FROM SecondTable t1
LEFT OUTER JOIN SecondTable t2
ON t1.ProductID = t2.ProductID
AND t2.FieldValue = 55
LEFT OUTER JOIN SecondTable t3
ON t1.ProductID = t3.ProductID
AND t3.FieldValue = 60
WHERE t1.FieldValue = 50
Then you can update all columns in the first table from this table in one update query. Note that you could make the above a View of the Second table so this would be easier to use later. (We'll call it SecondTableView for now; incidentally it has the exact form of the first table now).
UPDATE FirstTable
SET RatingID = t1.RatingID, Rating = t1.Rating, etc.
FROM SecondTableView t1
WHERE FirstTable.ProductID = t1.ProductID
The issue with this approach is that you must know all of the possible Fields for each Product ahead of time but that is pretty much required anyway because of the table schema being fixed.
Maybe something like this:
Update T1
Set T1.RatingID = T2.FieldID,
T1.Rating = T2.FieldValue
From Table1 T1
Inner JOin Table2 T2
On T1.ProductID = T2.ProductID
Where T2.FieldID = 50
To edit all the columns at once, you would need to use subqueries:
Update T1
Set T1.RatingID = (Select T2.FieldID
From Table2 T2
Where T2.FieldID = 50
And T2.ProductID = T1.ProductID)
From Table1 T1
Here is my situation:
Table one contains a set of data that uses an id for an unique identifier. This table has a one to many relationship with about 6 other tables such that.
Given Table 1 with Id of 001:
Table 2 might have 3 rows with foreign key: 001
Table 3 might have 12 rows with foreign key: 001
Table 4 might have 0 rows with foreign key: 001
Table 5 might have 28 rows with foreign key: 001
I need to write a report that lists all of the rows from Table 1 for a specified time frame followed by all of the data contained in the handful of tables that reference it.
My current approach in pseudo code would look like this:
select * from table 1
foreach(result) {
print result;
select * from table 2 where id = result.id;
foreach(result2) {
print result2;
}
select * from table 3 where id = result.id
foreach(result3) {
print result3;
}
//continued for each table
}
This means that the single report can run in the neighbor hood of 1000 queries. I know this is excessive however my sql-fu is a little weak and I could use some help.
LEFT OUTER JOIN Tables2-N on Table1
SELECT Table1.*, Table2.*, Table3.*, Table4.*, Table5.*
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.ID = Table2.ID
LEFT OUTER JOIN Table3 ON Table1.ID = Table3.ID
LEFT OUTER JOIN Table4 ON Table1.ID = Table4.ID
LEFT OUTER JOIN Table5 ON Table1.ID = Table5.ID
WHERE (CRITERIA)
Join doesn't do it for me. I hate having to de-tangle the data on the client side. All those nulls from left-joining.
Here's a set-based solution that doesn't use Joins.
INSERT INTO #LocalCollection (theKey)
SELECT id
FROM Table1
WHERE ...
SELECT * FROM Table1 WHERE id in (SELECT theKey FROM #LocalCollection)
SELECT * FROM Table2 WHERE id in (SELECT theKey FROM #LocalCollection)
SELECT * FROM Table3 WHERE id in (SELECT theKey FROM #LocalCollection)
SELECT * FROM Table4 WHERE id in (SELECT theKey FROM #LocalCollection)
SELECT * FROM Table5 WHERE id in (SELECT theKey FROM #LocalCollection)
Ah! Procedural! My SQL would look like this, if you needed to order the results from the other tables after the results from the first table.
Insert Into #rows Select id from Table1 where date between '12/30' and '12/31'
Select * from Table1 t join #rows r on t.id = r.id
Select * from Table2 t join #rows r on t.id = r.id
--etc
If you wanted to group the results by the initial ID, use a Left Outer Join, as mentioned previously.
You may be best off to use a reporting tool like Crystal or Jasper, or even XSL-FO if you are feeling bold. They have things built in to handle specifically this. This is not something the would work well in raw SQL.
If the format of all of the rows (the headers as well as all of the details) is the same, it would also be pretty easy to do it as a stored procedure.
What I would do: Do it as a join, so you will have the header data on every row, then use a reporting tool to do the grouping.
SELECT * FROM table1 t1
INNER JOIN table2 t2 ON t1.id = t2.resultid -- this could be a left join if the table is not guaranteed to have entries for t1.id
INNER JOIN table2 t3 ON t1.id = t3.resultid -- etc
OR if the data is all in the same format you could do.
SELECT cola,colb FROM table1 WHERE id = #id
UNION ALL
SELECT cola,colb FROM table2 WHERE resultid = #id
UNION ALL
SELECT cola,colb FROM table3 WHERE resultid = #id
It really depends on the format you require the data in for output to the report.
If you can give a sample of how you would like the output I could probably help more.
Join all of the tables together.
select * from table_1 left join table_2 using(id) left join table_3 using(id);
Then, you'll want to roll up the columns in code to format your report as you see fit.
What I would do is open up cursors on the following queries:
SELECT * from table1 order by id
SELECT * from table1 r, table2 t where t.table1_id = r.id order by r.id
SELECT * from table1 r, table3 t where t.table1_id = r.id order by r.id
And then I would walk those cursors in parallel, printing your results. You can do this because all appear in the same order. (Note that I would suggest that while the primary ID for table1 might be named id, it won't have that name in the other tables.)
Do all the tables have the same format? If not, then if you have to have a report that can display the n different types of rows. If you are only interested in the same columns then it is easier.
Most databases have some form of dynamic SQL. In that case you can do the following:
create temporary table from
select * from table1 where rows within time frame
x integer
sql varchar(something)
x = 1
while x <= numresults {
sql = 'SELECT * from table' + CAST(X as varchar) + ' where id in (select id from temporary table'
execute sql
x = x + 1
}
But I mean basically here you are running one query on your main table to get the rows that you need, then running one query for each sub table to get rows that match your main table.
If the report requires the same 2 or 3 columns for each table you could change the select * from tablex to be an insert into and get a single result set at the end...