update with subquery - sql

The code :
UPDATE tt_t_documents
SET t_Doc_header_ID = (SELECT
MIN(dh.Doc_header_ID)
FROM tt_t_documents td WITH (NOLOCK)
JOIN Doc_header dh WITH (NOLOCK)
ON dh.DH_doc_number = td.t_dh_doc_number
AND dh.DH_sub = 1
JOIN Pred_entry pe WITH (NOLOCK)
ON pe.Pred_entry_ID = dh.DH_pred_entry
JOIN Doc_type dt WITH (NOLOCK)
ON dty.Doc_type_ID = pe.PD_doc_type
AND dt.DT_mode = 5
HAVING COUNT(dh.Doc_header_ID) = 1);
I want to update my columns, but before that I also want to check if there is only one ID found.
The problem in this select is that I get more than one ID.
How can I write a query that updates each row and checks in the same query that there is only one id found?

I am guessing that you intend something like this:
update td
set t_Doc_header_ID = min_Doc_header_ID
from tt_t_documents td join
(select DH_doc_number, min(dh.Doc_header_ID) as min_Doc_header_ID
from Doc_header dh join
Pred_entry pe
on pe.Pred_entry_ID = dh.DH_pred_entry join
Doc_type dt
on dty.Doc_type_ID = pe.PD_doc_type and dt.DT_mode = 5
where dh.DH_doc_number = td.t_dh_doc_number and dh.DH_sub = 1
group by DH_doc_number
having count(dh.Doc_header_ID) = 1
) dh
on dh.DH_doc_number = td.t_dh_doc_number;
Using a join also means that you do not update the values where the condition does not match. If you use a left join, then the values will be updated to NULL (if that is your intention).

I'm not sure I believe you are getting more than one id back with that select given that you are doing a 'min' on it and have no group by. It should be only returning the lowest value for Doc_header_id.
To do what you are asking, first, you should have some way of joining to the tt_t_documents table in the update statement (eg. where td.id == tt_t_documents.id).
Second, you could re-write it to use the sub-query in the from. Something like:
update
tt_t_documents
set
t_Doc_header_ID = x.Doc_Header_id
from tt_t_documents join (
select td.id,
min(dh.Doc_header_ID)
from
tt_t_documents td
join Doc_header dh
on dh.DH_doc_number = td.t_dh_doc_number
and dh.DH_sub = 1
join Pred_entry pe
on pe.Pred_entry_ID = dh.DH_pred_entry
join Doc_type dt
on dty.Doc_type_ID = pe.PD_doc_type
and dt.DT_mode = 5
group by td.id
having
count(dh.Doc_header_ID) = 1
) x on tt_t_documents.id= x.id;
The syntax may not be perfect and I'm not sure how you want to find the doc_header_id but it would be something like this. The sub query would only return values with 1 doc_header_id). Not knowing the schema of your tables, this is as close as I can get.

Related

UPDATE statement with JOIN in SQL Server Not Working as Expected

I'm attempting to update the LAST_INSPECTION_FW field for all records in the VEHICLES_FW table with the last JOB_DATE_FW for records with the REASON_CODE_FW = 35. However, what's happening is that once the below code is executed, it's not taking into consideration the WHERE clause. This causes all of the records to update when it should just be updating those with the REASON_CODE_FW = 35.
Is there a way to restructure this code to get it working correctly? Please help, thanks!
UPDATE VEHICLES_FW
SET VEHICLES_FW.LAST_INSPECTION_FW = JOB_HEADERS_FW.FIELD2MAX
FROM VEHICLES_FW
INNER JOIN (SELECT VEHICLE_ID_FW, MAX(JOB_DATE_FW) AS FIELD2MAX
FROM JOB_HEADERS_FW
GROUP BY VEHICLE_ID_FW) AS JOB_HEADERS_FW
ON VEHICLES_FW.VEHICLE_ID_FW = JOB_HEADERS_FW.VEHICLE_ID_FW
INNER JOIN JOB_DETAILS_FW
ON JOB_NUMBER_FW = JOB_NUMBER_FW
WHERE REASON_CODE_FW = '35'
Common Table Expressions are your friend here. SQL Server's strange UPDATE ... FROM syntax is not. EG
with JOB_HEADERS_FW_BY_VEHICLE_ID as
(
SELECT VEHICLE_ID_FW, MAX(JOB_DATE_FW) AS FIELD2MAX
FROM JOB_HEADERS_FW
GROUP BY VEHICLE_ID_FW
), q as
(
Select VEHICLES_FW.LAST_INSPECTION_FW, JOB_HEADERS_FW_BY_VEHICLE_ID.FIELD2MAX NEW_LAST_INSPECTION_FW
FROM VEHICLES_FW
INNER JOIN JOB_HEADERS_FW_BY_VEHICLE_ID
ON VEHICLES_FW.VEHICLE_ID_FW = JOB_HEADERS_FW_BY_VEHICLE_ID.VEHICLE_ID_FW
INNER JOIN JOB_DETAILS_FW
ON JOB_NUMBER_FW = JOB_NUMBER_FW
WHERE REASON_CODE_FW = '35'
)
UPDATE q set LAST_INSPECTION_FW = NEW_LAST_INSPECTION_FW
I suspect this does what you want:
update v
set last_inspection_fw = (
select max(j.job_date_fw)
from job_headers_fw j
inner join job_details_fw jd on jd.job_number_fw = j.job_number_fw
where j.vehicle_id_fw = v.vehicle_id_fw and jd.reason_code_fw = 35
)
from vehicles_fw v

SQL: If there are two rows that contain same record, want it to display one

based on my question above, below is the SQL
SELECT ets_tools.tools_id, ets_borrower.fullname, ets_team.team_name, ets_borrow.time_from,
ets_borrow.time_to, ets_borrow.borrow_id FROM ets_tools
INNER JOIN ets_tools_borrow ON ets_tools.tools_id = ets_tools_borrow.tools_id
INNER JOIN ets_borrow ON ets_borrow.borrow_id = ets_tools_borrow.borrow_id
INNER JOIN ets_borrower ON ets_borrower.badgeid = ets_borrow.badgeid
INNER JOIN ets_team ON ets_team.team_id = ets_borrower.team_id
WHERE ets_tools.borrow_id IS NOT NULL AND ets_borrow.status_id = 1 AND ets_borrow.time_to IS NULL
and the result display like this:
From the image above, we can see that the borrow_id with value 1 display two rows. Now, how to display only one borrow_id for value 1 since its duplicate the same things.
Anyone can help?
Assuming you want to retain the record having the smallest tools_id, you could aggregate by the other columns and take the MIN of tools_id:
SELECT
MIN(ets_tools.tools_id) AS tools_id,
ets_borrower.fullname,
ets_team.team_name,
ets_borrow.time_from,
ets_borrow.time_to,
ets_borrow.borrow_id
FROM ets_tools
INNER JOIN ets_tools_borrow ON ets_tools.tools_id = ets_tools_borrow.tools_id
INNER JOIN ets_borrow ON ets_borrow.borrow_id = ets_tools_borrow.borrow_id
INNER JOIN ets_borrower ON ets_borrower.badgeid = ets_borrow.badgeid
INNER JOIN ets_team ON ets_team.team_id = ets_borrower.team_id
WHERE
ets_tools.borrow_id IS NOT NULL AND
ets_borrow.status_id = 1 AND
ets_borrow.time_to IS NULL
GROUP BY
ets_borrower.fullname,
ets_team.team_name,
ets_borrow.time_from,
ets_borrow.time_to,
ets_borrow.borrow_id;
Try this:
Change the SELECT to SELECT TOP 1 WITH TIES
And at the end add ORDER BY ROW_NUMBER() OVER(PARTITION BY ets_borrow.borrow_id ORDER BY ets_tools.tools_id)

Update multiple rows with different values based on Ids

I have a table called Todo. This contains a list of id's and a completedDate field which is NULL. There are some other columns but they are not used here.
I then have a query which returns the following data:
data
The query is from the same table and is as follows...
SELECT Todo.id, MIN(CloudCall.CloudCallHistory.CallStarted)
FROM Todo
JOIN CloudCall.CloudCallHistory ON CloudCall.CloudCallHistory.ObjectId = Todo.foreignId
JOIN CloudCall.CloudCallNotebookTypeCategoryLink ON CloudCall.CloudCallNotebookTypeCategoryLink.CategoryCode = CloudCall.CloudCallHistory.CategoryId
JOIN NotebookTypes ON NotebookTypes.NotebookTypeId = CloudCall.CloudCallNotebookTypeCategoryLink.NotebookTypeId
WHERE CloudCall.CloudCallHistory.CallStarted > Todo.foreignDate
AND Todo.completedDate IS NULL
AND Todo.cancelledDate IS NULL
AND NotebookTypes.NotebookFolderId = 175
AND CloudCall.CloudCallHistory.CategoryId != 17427
GROUP BY Todo.id
So what I want to do is update the Todo table with the new date where the id's match. Is there anyway to do this in 1 query?
It would look something like this maybe?:
UPDATE Todo
SET completedDate... (SELECT...)
WHERE id = ?
where the select would be the query that returned the data shown in the image. Thanks
Something like this
UPDATE Todo
SET completedDate = o.otherDate
FROM Todo t
INNER JOIN (SELECT otherDate, id FROM otherTable) AS o ON t.id = o.id
Update
To join against the query that collects the dates:
UPDATE Todo
SET completedDate = o.min_date
FROM Todo t
JOIN (SELECT Todo.id, MIN(CloudCall.CloudCallHistory.CallStarted) AS min_date
FROM Todo
JOIN CloudCall.CloudCallHistory ON CloudCall.CloudCallHistory.ObjectId = Todo.foreignId
JOIN CloudCall.CloudCallNotebookTypeCategoryLink ON CloudCall.CloudCallNotebookTypeCategoryLink.CategoryCode = CloudCall.CloudCallHistory.CategoryId
JOIN NotebookTypes ON NotebookTypes.NotebookTypeId = CloudCall.CloudCallNotebookTypeCategoryLink.NotebookTypeId
WHERE CloudCall.CloudCallHistory.CallStarted > Todo.foreignDate
AND Todo.completedDate IS NULL
AND Todo.cancelledDate IS NULL
AND NotebookTypes.NotebookFolderId = 175
AND CloudCall.CloudCallHistory.CategoryId != 17427
GROUP BY Todo.id) AS o ON o.id = t.id
Just use JOIN for this purposes:
UPDATE t1
SET t1.completedDate = t2.[Column]
FROM dbo.ToDo AS t1
INNER JOIN dbo.Table2 AS t2
ON t1.CommonField = t2.[Common Field]
WHERE t1.id in (...);
Try this:
UPDATE Todo SET completedDate=CloudCall.CloudCallHistory.CallStarted
FROM Todo
JOIN CloudCall.CloudCallHistory ON CloudCall.CloudCallHistory.ObjectId = Todo.foreignId
JOIN CloudCall.CloudCallNotebookTypeCategoryLink ON
CloudCall.CloudCallNotebookTypeCategoryLink.CategoryCode =
CloudCall.CloudCallHistory.CategoryId
JOIN NotebookTypes ON
NotebookTypes.NotebookTypeId = CloudCall.CloudCallNotebookTypeCategoryLink.NotebookTypeId
WHERE
CloudCall.CloudCallHistory.CallStarted > Todo.foreignDate AND
Todo.completedDate IS NULL AND Todo.cancelledDate IS NULL AND
NotebookTypes.NotebookFolderId = 175 AND
CloudCall.CloudCallHistory.CategoryId != 17427
GROUP BY Todo.id
Please use aliases, makes the query readable without eight pages of horizontal scrolling.
Because you're dealing with aggregate data, you'll need to use a variation on SQL Server's proprietary UPDATE FROM syntax:
;WITH aggData AS
(
SELECT Todo.id, MinStart = MIN(ch.CallStarted)
FROM Todo
INNER JOIN CloudCall.CloudCallHistory AS ch
ON ch.ObjectId = Todo.foreignId
INNER JOIN CloudCall.CloudCallNotebookTypeCategoryLink AS cl
ON cl.CategoryCode = ch.CategoryId
INNER JOIN NotebookTypes AS nt
ON nt.NotebookTypeId = cl.NotebookTypeId
WHERE ch.CallStarted > Todo.foreignDate
AND Todo.completedDate IS NULL
AND Todo.cancelledDate IS NULL
AND nt.NotebookFolderId = 175
AND ch.CategoryId <> 17427
GROUP BY Todo.id
)
UPDATE t SET completedDate = aggData.MinStart
FROM Todo AS t
INNER JOIN aggData
ON t.id = aggData.id;
You can probably simplify this so that Todo is only read once, but let's start with something that doesn't drastically rewrite the entire query.

ORA-00904 Invalid Identifier Error Teradata

I'm having trouble identifying the error in my code that occurred once I added an aggregate function to it. Each of the submitterclaimnumber items have multiple rows each with increasing values under the seqnumber column.
I'm trying to pull the row that has the highest seqnumber for each particular submitterclaimnumber.
For example. I have a submitterclaimnumber 999 with multiple rows. Each row is distinguished by a different value under seqnumber
so I have:
submitterclaimnumber = 999 seqnumber = 1
submitterclaimnumber = 999 seqnumber = 2
submitterclaimnumber = 999 seqnumber = 3
I only want the submitterclaimnumber = 999 seqnumber = 3 to show.
I can provide a bit more info on how the table is set up if needed.
The error I'm getting is:
"SELECT Failed. 904: ORA-00904: "MAX_SEQ": invalid identifier"
SELECT
CR.CLAIMNUMBER
,CR.LINENUMBER
,SUBSTR(CLM.SUBMITTERCLAIMNUMBER,3,11) AS CLM_NBR
,CR.SEQNUMBER
,MAX(CR.SEQNUMBER) AS MAX_SEQ
,CR.VOLUME
,CR.VOLUMETYPE
,CR.RATE
,CR.RATETYPE
,CR.ALLOWED
FROM PRICER.CLMREPRICINGDETAIL CR
INNER JOIN PRICER.CLMCLAIMS CLM
ON CLM.CLAIMNUMBER = CR.CLAIMNUMBER
INNER JOIN PRICER.CLMCLAIMITEMS CLMP
ON CLM.CLAIMNUMBER = CLMP.CLAIMNUMBER
AND CLM.OWNERID = CLMP.OWNERID
INNER JOIN PRICER.CLMINSURED CLMI
ON CLM.CLAIMNUMBER = CLMI.CLAIMNUMBER
AND CLM.OWNERID = CLMI.OWNERID
WHERE
TRADEPARTNERID IN ('NationalNonHouse','NationalHouse')
AND SUBSTR(CLM.SUBMITTERCLAIMNUMBER,3,11) IN ('17304CD2083','17297CA4107')
GROUP BY 1,2,3,4,5,6,7,8,9,10
HAVING CR.SEQNUMBER = MAX_SEQ
;
You might be using Teradata's SQL Assistant to query your Oracle system, but you're not using it against a Teradata DBMS :-)
To get the row with the max SEQNUMBER per SUBMITTERCLAIMNUMBER you can't use HAVING, because after aggregation there's no more individual row to compare to. But Windowed Aggregate allow that:
SELECT *
FROM
(
SELECT
CR.CLAIMNUMBER
,CR.LINENUMBER
,SUBSTR(CLM.SUBMITTERCLAIMNUMBER,3,11) AS CLM_NBR
,CR.SEQNUMBER
,MAX(CR.SEQNUMBER) OVER (PARTITON BY SUBMITTERCLAIMNUMBER) AS MAX_SEQ
,CR.VOLUME
,CR.VOLUMETYPE
,CR.RATE
,CR.RATETYPE
,CR.ALLOWED
FROM PRICER.CLMREPRICINGDETAIL CR
INNER JOIN PRICER.CLMCLAIMS CLM
ON CLM.CLAIMNUMBER = CR.CLAIMNUMBER
INNER JOIN PRICER.CLMCLAIMITEMS CLMP
ON CLM.CLAIMNUMBER = CLMP.CLAIMNUMBER
AND CLM.OWNERID = CLMP.OWNERID
INNER JOIN PRICER.CLMINSURED CLMI
ON CLM.CLAIMNUMBER = CLMI.CLAIMNUMBER
AND CLM.OWNERID = CLMI.OWNERID
WHERE
TRADEPARTNERID IN ('NationalNonHouse','NationalHouse')
AND SUBSTR(CLM.SUBMITTERCLAIMNUMBER,3,11) IN ('17304CD2083','17297CA4107')
-- no more GROUP BY
) dt
WHERE SEQNUMBER = MAX_SEQ
;
Btw, in Teradata SQL you could remove the Derived Table and simply use QUALIFY. Then it looks quite close to your original syntax:
QUALIFY
CR.SEQNUMBER
= MAX(CR.SEQNUMBER) OVER (PARTITON BY CLM.SUBMITTERCLAIMNUMBER)
You can't use alias inside a query, you have to use the MAX(CR.SEQNUMBER) in your HAVING clause
you can't compare a not aggregate row like CR.SEQNUMBER inside a HAVING
:
SELECT
CR.CLAIMNUMBER
,CR.LINENUMBER
,SUBSTR(CLM.SUBMITTERCLAIMNUMBER,3,11) AS CLM_NBR
,CR.SEQNUMBER
,MAX(CR.SEQNUMBER) AS MAX_SEQ
,CR.VOLUME
,CR.VOLUMETYPE
,CR.RATE
,CR.RATETYPE
,CR.ALLOWED
FROM PRICER.CLMREPRICINGDETAIL CR
INNER JOIN PRICER.CLMCLAIMS CLM
ON CLM.CLAIMNUMBER = CR.CLAIMNUMBER
INNER JOIN PRICER.CLMCLAIMITEMS CLMP
ON CLM.CLAIMNUMBER = CLMP.CLAIMNUMBER
AND CLM.OWNERID = CLMP.OWNERID
INNER JOIN PRICER.CLMINSURED CLMI
ON CLM.CLAIMNUMBER = CLMI.CLAIMNUMBER
AND CLM.OWNERID = CLMI.OWNERID
WHERE
TRADEPARTNERID IN ('NationalNonHouse','NationalHouse')
AND SUBSTR(CLM.SUBMITTERCLAIMNUMBER,3,11) IN ('17304CD2083','17297CA4107')
GROUP BY
CR.CLAIMNUMBER
,CR.LINENUMBER
,SUBSTR(CLM.SUBMITTERCLAIMNUMBER,3,11)
,CR.SEQNUMBER
,CR.VOLUME
,CR.VOLUMETYPE
,CR.RATE
,CR.RATETYPE
,CR.ALLOWED
-- HAVING CR.SEQNUMBER = MAX(CR.SEQNUMBER) -- not allowed
;

Only return value that matches the ID on table 1

I have tried all possible joins and sub-queries but I cant get the data to only return one value from table 2 that exactly matches the vendor ID. If I dont have the address included in the query, I get one hit for the vendor ID. How can I make it so that when I add the address, I only want the one vendor that I get prior to adding the address.
The vendor from table one must be VEN-CLASS IS NOT NULL.
This was my last attempt using subquery:
SELECT DISTINCT APVENMAST.VENDOR_GROUP,
APVENMAST.VENDOR,
APVENMAST.VENDOR_VNAME,
APVENMAST.VENDOR_CONTCT,
APVENMAST.TAX_ID,
Subquery.ADDR1
FROM (TEST.dbo.APVENMAST APVENMAST
INNER JOIN
(SELECT APVENADDR.ADDR1,
APVENADDR.VENDOR_GROUP,
APVENADDR.VENDOR,
APVENMAST.VEN_CLASS
FROM TEST.dbo.APVENADDR APVENADDR
INNER JOIN TEST.dbo.APVENMAST APVENMAST
ON (APVENADDR.VENDOR_GROUP = APVENMAST.VENDOR_GROUP)
AND (APVENADDR.VENDOR = APVENMAST.VENDOR)
WHERE (APVENMAST.VEN_CLASS IS NOT NULL)) Subquery
ON (APVENMAST.VENDOR_GROUP = Subquery.VENDOR_GROUP)
AND (APVENMAST.VENDOR = Subquery.VENDOR))
INNER JOIN TEST.dbo.APVENLOC APVENLOC
ON (APVENMAST.VENDOR_GROUP = APVENLOC.VENDOR_GROUP)
AND (APVENMAST.VENDOR = APVENLOC.VENDOR)
WHERE (APVENMAST.VEN_CLASS IS NOT NULL)
Try this:
SELECT APVENMAST.VENDOR_GROUP
, APVENMAST.VENDOR
, APVENMAST.VENDOR_VNAME
, APVENMAST.VENDOR_CONTCT
, APVENMAST.TAX_ID
, APVENADDR.ADDR1
FROM TEST.dbo.APVENMAST APVENMAST
INNER JOIN (
select VENDOR_GROUP, VENDOR, ADDR1
, row_number() over (partition by VENDOR_GROUP, VENDOR order by ADDR1) r
from TEST.dbo.APVENADDR
) APVENADDR
ON APVENADDR.VENDOR_GROUP = APVENMAST.VENDOR_GROUP
AND APVENADDR.VENDOR = APVENMAST.VENDOR
AND APVENADDR.r = 1
--do you need this table; you're not using it...
--INNER JOIN TEST.dbo.APVENLOC APVENLOC
--ON APVENMAST.VENDOR_GROUP = APVENLOC.VENDOR_GROUP
--AND APVENMAST.VENDOR = APVENLOC.VENDOR
WHERE APVENMAST.VEN_CLASS IS NOT NULL
--if the above inner join was to filter results, you can do this instead:
and exists (
select top 1 1
from TEST.dbo.APVENLOC APVENLOC
ON APVENMAST.VENDOR_GROUP = APVENLOC.VENDOR_GROUP
AND APVENMAST.VENDOR = APVENLOC.VENDOR
)
I found another column in the APVENLOC table that I can filter on to get the unique vendor. Turns out if the vendor address is for the main office, the vendor location is set blank.
Easier than I thought it would be!
SELECT DISTINCT APVENMAST.VENDOR_GROUP,
APVENMAST.VENDOR,
APVENMAST.VENDOR_VNAME,
APVENADDR.ADDR1,
APVENMAST.VENDOR_SNAME,
APVENADDR.LOCATION_CODE,
APVENMAST.VEN_CLASS
FROM TEST.dbo.APVENMAST APVENMAST
INNER JOIN TEST.dbo.APVENADDR APVENADDR
ON (APVENMAST.VENDOR_GROUP = APVENADDR.VENDOR_GROUP)
AND (APVENMAST.VENDOR = APVENADDR.VENDOR)
WHERE (APVENADDR.LOCATION_CODE = ' ')
Shaji