Updating row of table Using data from multiple columns of another table - sql

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

Related

Join table in oracle database

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

UPDATE query in ACCESS with AVERAGE value from other table

I have these two simple access tables.
Table 1 consists of an Item description and weight column. Item description is not unique as weight slightly varies.
Table 2 is designed to hold the average weight of the items in table 1. In this table the item description is unique.
I cannot make the UPDATE query in ACCESS work. It says the query does not include weight as part of an aggregate function.
When I attempt to add a GROUP BY clause, it still does not work due to syntax errors..
Anyone have an idea as to what might be the issue?
Here is the query:
UPDATE TABLE2
INNER JOIN TABLE1 ON TABLE2.DESCRIPTION = TABLE1.DESCRIPTION
SET TABLE2.WEIGHT = AVG(TABLE1.WEIGHT)
UPDATE t2
SET t2.WEIGHT = t1.AVG_WEIGHT
FROM TABLE2 t2
INNER JOIN (select TABLE1.DESCRIPTION,AVG(TABLE1.WEIGHT) as AVG_WEIGHT
from TABLE1
group by TABLE1.DESCRIPTION) as t1
on TABLE2.DESCRIPTION = TABLE1.DESCRIPTION
There is simpler way here-
You need to create a staging or inter-mediate table to calculate the average.
You must create a new table t3,from query-
select TABLE1.DESCRIPTION,AVG(TABLE1.WEIGHT) as AVG_WEIGHT
from TABLE1
group by TABLE1.DESCRIPTION
And then you can use it in update query
UPDATE TABLE2
INNER JOIN TABLE3 ON TABLE3.DESCRIPTION = TABLE2.DESCRIPTION
SET TABLE3.WEIGHT = TABLE2.WEIGHT

When using UPDATE and SET with SQL rows appear to be missing

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 (****)

SQL JOIN + GROUPBY select data from row with MAX(Date)

I'm having trouble figuring out the solution to this SQL query.
Schema
Edit: Adding Item Table
Item Table
PK ItemID
lots of other columns
Linking Table
FK ItemID uniqueidentifier
FK TransactionID uniqueidentifier
Transaction Table
PK ID uniqueidentifier
EntryDateTime DateTime
(several other rows of int, varchar...)
Edit : I think I haven't made the relationships clear. Each ITEM (table not shown) can have multiple transactions. Multiple items can share the same transaction (hence the linking table).
Please see the bottom for my current Query. I have left this striked to show the progression of the question.
I want to do something like this query. The trick is I want the t.varchar and t.int columns to be whatever values are in the MAX(t.EntryDateTime) row. I don't even know if group by is the right way to do this query.
SELECT lt.ItemID, MAX(t.EntryDateTime), t.varchar, t.int
FROM LinkingTable lt
LEFT JOIN Transactions t ON lt.TransactionID = t.ID
GROUP BY lt.ItemID
This table is going to be joined against in this SQL query, so please try and give me the most performant solution . Assume Table1 will contain millions of records.
SELECT
(many columns)
FROM Table1
LEFT JOIN Table2 ON Table1.Table2ID = Table2.ID
LEFT JOIN Table3 ON ....
LEFT JOIN Table4 ON (Table2.ID = Table4.Table2ID and Table4.LocaleID = 127 and Table4.Type = 0)
LEFT JOIN **the query above** AS vTable1 ON vTable1.ItemID = Table1.ID
WHERE Table1.CheckID IN (SELECT ID FROM Checks WHERE ....)
Edit : This is the query I have that is working, but I'm not sure its the most efficient. LinkingTable has ~ 200k records and its taking 6 seconds to run.
SELECT DISTINCT lt.ItemID, t.EntryDateTime, t.varchar, t.int
FROM LinkingTable lt
LEFT JOIN Transactions t ON t.id = (SELECT Top 1 t2.id FROM LinkingTable lt2
LEFT JOIN Transactions t2 on lt2.TransactionID = t2.ID
where lt2.ItemID = lt.ItemID ORDER BY t2.PrintTime DESC)
Try this,
SELECT i.*, outerT.EntryDateTime, outerT.varchar, outerT.int
FROM Item i
LEFT JOIN
(SELECT ItemId AS outerItemId, EntryDateTime, varchar, int
FROM (SELECT ROW_NUMBER() OVER (PARTITION BY lt.ItemId ORDER BY t.EntryDateTime) AS RowNumber, lt.ItemId, t.EntryDateTime, t.varchar, t.int
FROM Tranaction t INNER JOIN LinkingTable lt ON lt.TransactionId = t.ID) innerT
WHERE RowNumber = 1) outerT ON outerT.outerItemId = Item.ID
Hope this solves your problem
Even with a million plus records, you will have some performance hits, but I would ensure and index on the transaction table based on the ( ItemID, Primary Key ). The reason Primary Key and not the date -- if its auto-incremented, and it's date/time stamped at time the transaction occurs, they will be in-essence, one-in-the-same. The last entry in the file will always have the latest date. That said, an ID column should be faster with index than a date/time. This also prevents need of looking at BOTH elements of most recent date, and the transaction ID associated with that date. Here is how I would FIRST attempt the query.
select
I.*,
T2.*
from
Item I
JOIN
( select T.ItemID, MAX( T.PrimaryKey ) as LastEntryPerItem
from Transactions T
group by T.ItemID ) MaxPerItem
ON I.ItemID = T.ItemID
JOIN Transactions T2
on MaxPerItem.LastEntryPerItem = T2.PrimaryKey
order by
whatever
select lt.ItemId, t.entrydatetime, t.varchar, t.int
from LinkingTable lt
left join transactions t
on lt.transactionId = t.id
and t.entryDateTime = (select max(t.EntryDateTime)
from transactions t2
where t2.id = t.id)
I had a similar question before
( SQL Join to get value belong with most recent date). There's another solution by JNK involving two joins which may be faster. I've posted below. You'll need to test to see which performs better.
select lt.ItemId, t.entrydatetime, t.varchar, t.int
from LinkingTable lt
inner join transactions t
on lt.ItemId= t.ItemId
Inner join (SELECT ItemId, MAX(entrydatetime) entrydatetime
FROM transactions t2
GROUP BY ItemId) SubQ
ON SubQ.ItemId= t.ItemId
AND SubQ.entrydatetime= t.entrydatetime
Why don't you create a view that has all your "many columns" and then run a query against that view?

SQL Server - Select Left Join NULL record WHERE condition

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).