How to test the following SQL script in Oracle 11g - sql

Below is a script written in Oracle 11g:
MERGE INTO tblbio t
USING (SELECT e.id, tblduplicate.cpid, e.bdt,e.LN, e.FN
FROM tblduplicate, entities where
trim(e.id) = trim(tblduplicate.id)) source
ON (t.cpid = source.cpid and trim(t.bdt) = trim(source.bdt))
WHEN MATCHED
THEN
UPDATE SET t.id = source.id, t.stat = '4'
WHERE t.cmp = 'HHCC'
AND t.thn = '2013'
AND trim(lower(source.LN)) = trim(lower(t.LN))
AND trim(lower(source.FN)) = trim(lower(t.FN))
AND nvl(trim(t.bdt), ' ') <> ' '
AND t.bdt <> '00000000'
AND nvl(trim(source.bdt), ' ') <> ' '
and source.bdt <> '00000000'
AND t.stat <> '4'
Due to my data integrity problem, this script once in awhile will generate more than 1 records. In which this script will generate error.
I want to create validation prior this script is run. When my validation generate more than 1 records I stop this script from running. How can I do that?
I tried to write the following for my validation to capture record count but Oracle just did not like it.
select* from tblbio t <-----
USING (SELECT e.id, tblduplicate.cpid, e.bdt,e.LN, e.FN
FROM tblduplicate, entities where
trim(e.id) = trim(tblduplicate.id)) source
ON (t.cpid = source.cpid and trim(t.bdt) = trim(source.bdt))
WHEN MATCHED
THEN
select * from tblbio t <-----
WHERE t.cmp = 'HHCC'
AND t.thn = '2013'
AND trim(lower(source.LN)) = trim(lower(t.LN))
AND trim(lower(source.FN)) = trim(lower(t.FN))
AND nvl(trim(t.bdt), ' ') <> ' '
AND t.bdt <> '00000000'
AND nvl(trim(source.bdt), ' ') <> ' '
and source.bdt <> '00000000'
AND t.stat <> '4'

Converting original MERGE query to select from tbiblio:
MERGE INTO ... USING --> select from ... join
WHEN MATCHED THEN UPDATE SET ... WHERE --> WHERE
Final query:
select * from tblbio t
JOIN (SELECT e.id, tblduplicate.cpid, e.bdt,e.LN, e.FN
FROM tblduplicate, entities where
trim(e.id) = trim(tblduplicate.id)) source
ON (t.cpid = source.cpid and trim(t.bdt) = trim(source.bdt))
WHERE t.cmp = 'HHCC'
AND t.thn = '2013'
AND trim(lower(source.LN)) = trim(lower(t.LN))
AND trim(lower(source.FN)) = trim(lower(t.FN))
AND nvl(trim(t.bdt), ' ') <> ' '
AND t.bdt <> '00000000'
AND nvl(trim(source.bdt), ' ') <> ' '
and source.bdt <> '00000000'
AND t.stat <> '4'

Related

Update SQL for Oracle when more then two tables are involved - ORA-00933

I am relatively new to writing SQL. I have written the following:
UPDATE PS_EMAIL_ADDRESSES C
SET C.EMAIL_ADDR = B.SFA_CR_EMAIL_ADDR
FROM PS_LOAN_ORIGNATN A, PS_SFA_COD_BORROWR B
WHERE A.EMPLID <> A.BORR_EMPLID
AND A.AID_YEAR = '2019'
AND A.ACAD_CAREER = 'UGRD'
AND A.BORR_EMPLID <> ' '
AND B.SFA_CR_AWARD_YEAR = A.AID_YEAR
AND B.SFA_CR_ID_SSN = A.BORR_SSN
AND B.SFA_CR_EMAIL_ADDR <> ' '
AND B.SFA_CR_DOCUMENT_ID = (SELECT MAX( D.SFA_CR_DOCUMENT_ID)
FROM PS_SFA_COD_BORROWR D
WHERE D.SFA_CR_AWARD_YEAR = B.SFA_CR_AWARD_YEAR
AND D.SFA_CR_ID_SSN = B.SFA_CR_ID_SSN
AND D.SFA_CR_EMAIL_ADDR <> ' ')
AND EXISTS (SELECT 1
FROM PS_EMAIL_ADDRESSES C
WHERE C.EMPLID = A.BORR_EMPLID
AND C.E_ADDR_TYPE = 'PEM'
AND C.EMAIL_ADDR <> B.SFA_CR_EMAIL_ADDR)
But checking it on SQL Fiddle, I get ORA-00933. I may not be asking it right, but I have been working through different versions.
A few issues: That isnt Oracle Sql syntax, you have reused the alias C, and your indenting is a bit hard to follow.
See if this works for you:
UPDATE PS_EMAIL_ADDRESSES E
SET E.EMAIL_ADDR = (SELECT B.SFA_CR_EMAIL_ADDR
FROM PS_LOAN_ORIGNATN A, PS_SFA_COD_BORROWR B
WHERE A.EMPLID <> A.BORR_EMPLID
AND A.AID_YEAR = '2019'
AND A.ACAD_CAREER = 'UGRD'
AND A.BORR_EMPLID <> ' '
AND B.SFA_CR_AWARD_YEAR = A.AID_YEAR
AND B.SFA_CR_ID_SSN = A.BORR_SSN
AND B.SFA_CR_EMAIL_ADDR <> ' '
AND B.SFA_CR_DOCUMENT_ID = (SELECT MAX( D.SFA_CR_DOCUMENT_ID)
FROM PS_SFA_COD_BORROWR D
WHERE D.SFA_CR_AWARD_YEAR = B.SFA_CR_AWARD_YEAR
AND D.SFA_CR_ID_SSN = B.SFA_CR_ID_SSN
AND D.SFA_CR_EMAIL_ADDR <> ' ')
AND EXISTS (SELECT 1
FROM PS_EMAIL_ADDRESSES C
WHERE C.EMPLID = A.BORR_EMPLID
AND C.E_ADDR_TYPE = 'PEM'
AND C.EMAIL_ADDR <> B.SFA_CR_EMAIL_ADDR)
);

Why query takes more time to execute when I use if else statement in stored procedure?

I have used below query in my stored procedure. When I am using if else statement in the stored procedure query takes more time to execute. When I remove if else statement query takes less time to return result.
If #P_Make = 'ALL' Or IsNull(#P_Make, '') = ''
Begin
Set #P_Make = '%';
End
Else
Begin
Set #P_Make = #P_Make + '%';
End
If #P_Model = 'ALL' Or IsNull(#P_Model, '') = ''
Begin
Set #P_Model = '%';
End
Else
Begin
Set #P_Model = #P_Model + '%';
End
If #P_Location = 'ALL' Or IsNull(#P_Location, '') = ''
Begin
Set #P_Location = '%';
End
Else
Begin
Set #P_Location = #P_Location + '%';
End
If #P_City = 'ALL' Or IsNull(#P_City, '') = ''
Begin
Set #P_City = '%';
End
Else
Begin
Set #P_City = #P_City + '%';
End
If #P_Category = 'ALL' Or IsNull(#P_Category, '') = ''
Begin
Set #P_Category = '%';
End
Else
Begin
Set #P_Category = Case When #P_Category = 'Bikes & Scooters' Then '2W%'
When #P_Category = '3 Wheelers' Then '3W%'
When #P_Category = 'Cars & SUVs' Then '4W%'
When #P_Category = 'Trucks' Then 'CV%'
When #P_Category = 'Farm Equipments' Then 'FE%'
When #P_Category = 'Industrial Equipments' Then 'IE%'
When #P_Category = 'Construction Equipments' Then 'CE%'
Else #P_Category + '%'
End
End
If #P_Service = 'ALL' Or IsNull(#P_Service, '') = ''
Begin
Set #P_Service = ''
End
Select Count(C.Sal_Pk_Id)
From dbo.Auction (NoLock) A
Inner Join dbo.PLACE (NoLock) B On A.Auc_Place_Fk_Id = B.Place_Pk_Id
Inner Join dbo.SALES_DETAILS (NoLock) C On A.Auc_Code = C.Sal_Auc_Code
Inner Join dbo.AUCTIONSERVICES (NoLock) D On C.Sal_Regno = D.Auc_Service_Regno And C.Sal_Auc_Code = D.Auc_Service_Auctioncode
Inner Join dbo.LOT (NoLock) E On C.Sal_Lot_No = E.Lot_Lot_No And C.Sal_Auc_Code = E.Lot_Auc_Code
Inner Join dbo.INVENTORY (NoLock) F On C.Sal_Regno = F.Inv_H_Reg_No
Where C.[Status] = 'L' And E.Lot_Sold_Status = 'S'
And IsNull(B.Place_City, '') Like #P_City And IsNull(B.Place_State, '') Like #P_Location
And IsNull(F.Inv_H_Category, '') Like #P_Category And IsNull(F.Inv_H_Mfg_Name, '') Like #P_Make
And IsNull(F.Inv_H_Model, '') Like #P_Model
Query without if else statement:
Select Count(C.Sal_Pk_Id)
From dbo.Auction (NoLock) A
Inner Join dbo.PLACE (NoLock) B On A.Auc_Place_Fk_Id = B.Place_Pk_Id
Inner Join dbo.SALES_DETAILS (NoLock) C On A.Auc_Code = C.Sal_Auc_Code
Inner Join dbo.AUCTIONSERVICES (NoLock) D On C.Sal_Regno = D.Auc_Service_Regno And C.Sal_Auc_Code = D.Auc_Service_Auctioncode
Inner Join dbo.LOT (NoLock) E On C.Sal_Lot_No = E.Lot_Lot_No And C.Sal_Auc_Code = E.Lot_Auc_Code
Inner Join dbo.INVENTORY (NoLock) F On C.Sal_Regno = F.Inv_H_Reg_No
Where C.[Status] = 'L' And E.Lot_Sold_Status = 'S'
And (#P_City = '' Or IsNull(B.Place_City, '') = #P_City)
And (#P_Location= '' Or IsNull(B.Place_State, '') = #P_Location)
And (#P_Category = '' Or IsNull(F.Inv_H_Category, '') = #P_Category)
And (#P_Make = '' Or IsNull(F.Inv_H_Mfg_Name, '') = #P_Make)
And (#P_Model = '' Or IsNull(F.Inv_H_Model, '') = #P_Model)
Why the query output takes less time when I remove if else statement in the stored procedure?
There's two main reasons - like and execution plan.
like '%something' is the slowest possible thing you can filter by - it means going row by row, reading the whole data in the column and doing a comparison. It can't use any index seeks, only scans.
Second, you only get one execution plan, based on the first way the procedure is called. This pretty much guarantees that for any other input data, the performance will suffer. In your second example, while the plan is again sub-optimal and dependent on the initial inputs, it doesn't completely ignore all the possible filters - it just optimizes based on statistics.
Dynamic filters aren't something nobody tried to solve before. Learn from the best: http://www.sommarskog.se/dyn-search.html
I think the query without if else is faster because it does not have a Like operator
Where C.[Status] = 'L' And E.Lot_Sold_Status = 'S'
And IsNull(B.Place_City, '') Like #P_City And IsNull(B.Place_State, '') Like #P_Location
And IsNull(F.Inv_H_Category, '') Like #P_Category And IsNull(F.Inv_H_Mfg_Name, '') Like #P_Make
And IsNull(F.Inv_H_Model, '') Like #P_Model
and I suppose that the second query can use indexes more efficiently. The first and second queries are different, please look at Actual Execution Plan and you will realize why it happens.

Concatenating 2 columns in sql and applying like operation on the result

SELECT
CONCAT(SG.firstname +' ', SG.lastName) AS 'Name',
SG.entityId 'ID',
dbo.GetTitleDescriptionByEntityID(SG.entityId) 'Title',
CASE ISNULL(EC.address2, '')
WHEN '' THEN EC.address1
ELSE EC.address1 +', ' + EC.address2
END +', ' + EC.city + ', ' + LDState.ItemName + ', ' + EC.zip 'Address',
CR.clinicalRate
FROM
StaffGeneral SG WITH (NOLOCK)
JOIN
LookupDetails LD WITH (NOLOCK) ON SG.employeeStatus = LD.lookupDetailsId AND LD.ItemAbbreviation = 'SCSSC'
JOIN
othersRating or1 WITH (nolock) ON SG.entityId = or1.entityId AND dateRated = (SELECT TOP 1 MAX(dateRated) FROM OthersRating r1 WITH (nolock) WHERE r1.entityid = SG.entityId)
JOIN
LookupDetails LDRating with (nolock) ON or1.rating = LDRating.lookupDetailsId AND LDRating.ItemAbbreviation NOT IN ('RTNP', 'RTDNU', 'RTPENDING', 'RTDELETE', 'RTTERMI')
JOIN
EntityContacts EC with (nolock) ON SG.entityId = EC.entityId AND (eC.contactId = 'General' or EC.contactId = 'General1') AND EC.deleted = 0
JOIN
LookupDetails LDState WITH (NOLOCK) ON EC.state = LDState.lookupDetailsId
JOIN
ContractorRate CR ON SG.entityId = CR.entityId AND CR.deleted = 0
WHERE
SG.isActive = 0 AND SG.deleted = 0
AND ((SG.entityId = #subContractorID) OR (#subContractorID IS null))
AND ((#subContractorName IS NULL) OR ((EC.firstName + ' ' + EC.lastName) LIKE #subContractorName + '%'))
In the above query I wanted to concat the first and last name with space and query it against the input to the stored procedure #subcontractorname.
When the subcontractorname is null that is fine but I am not able to get results when it jumps to Or clause .
Tried the SQL Server Profiler but does not show anything how the query is formed
I tried executing the SQL by putting the whole SQL in a var but I am hitting the problem of varchar(max).
Please advise

SQL Error The multi-part identifier could not be bound

When I'm trying to execute this query, I get "The multi-part identifier "#EachEmployee.ResultID" could not be bound." error.
DECLARE #QueryText VARCHAR(1000)
SET #QueryText = '
UPDATE #EachEmployee2
SET #EachEmployee2.CorrectAnswerCount = (
SELECT COUNT (TMID)
FROM
' + #WorkDatabaseName + '.dbo.TestBlockTM AS TBTM,
' + #WorkDatabaseName + '.dbo.TestResultTM AS TRTM
WHERE 1 = 1
AND TBTM.TMID = TRTM.OtvetID
AND TBTM.TMPID = TRTM.VoprosID
AND TRTM.TestResultID = #EachEmployee2.ResultID
)
WHERE
#EachEmployee2.IsGroup = 0 AND #EachEmployee2.BlockID = 1'
EXECUTE(#QueryText)
However, the similar query is working perfectly:
UPDATE #EachEmployee2
SET #EachEmployee2.ResultID = (
SELECT TOP 1 TestResultID
FROM #AnswersList AS a
WHERE 1 = 1
AND a.SID = #EachEmployee2.SID
AND a.UserID = #EachEmployee2.UserID
)
Can someone tell what's a problem here? Thanks and appreciate your help.
try this
SET #QueryText = '
UPDATE emp
SET emp.CorrectAnswerCount = (
SELECT COUNT (TMID)
FROM
' + #WorkDatabaseName + '.dbo.TestBlockTM AS TBTM,
' + #WorkDatabaseName + '.dbo.TestResultTM AS TRTM
WHERE 1 = 1
AND TBTM.TMID = TRTM.OtvetID
AND TBTM.TMPID = TRTM.VoprosID
AND TRTM.TestResultID = emp.ResultID
)
From #EachEmployee2 emp
WHERE
emp.IsGroup = 0 AND emp.BlockID = 1'
Can you try this query and tell me if the error still occurs:
SET #QueryText = '
UPDATE #EachEmployee2
SET CorrectAnswerCount = (
SELECT COUNT (TMID)
FROM
' + #WorkDatabaseName + '.dbo.TestBlockTM AS TBTM,
INNER JOIN ' + #WorkDatabaseName + '.dbo.TestResultTM AS TRTM ON TRTM.OtvetID = TBTM.TMID
AND TRTM.VoprosID = TBTM.TMPID
AND TRTM.TestResultID = E.ResultID
)
FROM #EachEmployee2 E
WHERE E.IsGroup = 0
AND E.BlockID = 1'
I added the FROM clause and modified a bit the subquery in order to replace the old SQL syntax by an explicit INNER JOIN clause.
Hope this will help

Concatenating row values using Inner Join

I have this query that I'm using to join two tables for an update statement. This is the query that I built:
DECLARE #DocHoldReasons VARCHAR(8000)
SET #DocHoldReasons = 'DocType Hold'
UPDATE dbo.EpnPackages
SET Error = 1, Msg = COALESCE (#DocHoldReasons + ': ', '') + cv.Value
FROM EpnPackages p
INNER JOIN EpnCountyValues cv ON cv.CountyId = p.CountyId and cv.ValueName = 'DocHoldReason'
WHERE p.Status = 1000
AND p.Error = 0
There are two rows in the EpnCountyValues table with the same ValueName, and I need them both concatenated, and I'm having a tough time doing it. All I can get is the first row value. This is what my resulting string should look like - 'DocType Hold: Test: Test.124.'
eidt: I need the rows with the same ValueName to be concatenated for the update query. There could be more than two rows with ValueName = DocHoldReason
Here's the structure of the EpnCountyValues table:
CountyValueId CountyId ValueName Value
1 1 DocHoldReason Test
2 2 xyz Test1
3 3 DocHoldReason Test.124.
Any help would be greatly appreciated. Thanks!
You can do what you want by pre-aggregating the table before the join. If there are only two values and you don't care about the order, then this will work:
DECLARE #DocHoldReasons VARCHAR(8000);
SET #DocHoldReasons = 'DocType Hold';
UPDATE dbo.EpnPackages
SET Error = 1,
Msg = (COALESCE(#DocHoldReasons + ': ', '') + minv +
(case when minv <> maxv then ': ' + maxv else '' end)
)
FROM EpnPackages p INNER JOIN
(select cv.CountyId, min(cv.value) as minv, max(cv.value) as maxv
from EpnCountyValues cv
where cv.ValueName = 'DocHoldReason'
) cv
ON cv.CountyId = p.CountyId
WHERE p.Status = 1000 AND p.Error = 0;
EDIT:
For more than two values, you have to do string concatenation. That is "unpleasant" in SQL Server. Here is the approach:
DECLARE #DocHoldReasons VARCHAR(8000);
SET #DocHoldReasons = 'DocType Hold';
UPDATE dbo.EpnPackages
SET Error = 1,
Msg = (COALESCE(#DocHoldReasons + ': ', '') +
stuff((select ': ' + cv.value
from EpnCountyValues cv
where cv.ValueName = 'DocHoldReason' and
cv.CountyId = p.CountyId
for xml path ('')
), 1, 2, '')
)
WHERE p.Status = 1000 AND p.Error = 0;
This version does it using a correlated subquery rather than a join with an aggregation.
EDIT II:
You can fix this with an additional coalesce:
DECLARE #DocHoldReasons VARCHAR(8000);
SET #DocHoldReasons = 'DocType Hold';
UPDATE dbo.EpnPackages
SET Error = 1,
Msg = (COALESCE(#DocHoldReasons + ': ', '') +
COALESCE(stuff((select ': ' + cv.value
from EpnCountyValues cv
where cv.ValueName = 'DocHoldReason' and
cv.CountyId = p.CountyId
for xml path ('')
), 1, 2, ''), '')
)
WHERE p.Status = 1000 AND p.Error = 0;
You can join the EpnCountyValues table twice!
Something like this,
DECLARE #DocHoldReasons VARCHAR(8000)
SET #DocHoldReasons = 'DocType Hold'
Select #DocHoldReasons + ': ' + ev1.Value + ': ' + ev2.Value
From EpnPackages p
Join EpnCountyValues ev1 on p.packageID = ev1.packageID
Join EpnCountyValues ev2 on ev1.ValueName = ev2.ValueName
Where ev1.ValueName = 'DocHoldRegion'
And p.status = 1000
And p.error = 0
Does that select look right to you? If so, run the update. I always try to run a select before an update. I have to admit, I'm a bit confused why you're joining on CountyID, but then it seems like you'll be updating the values from different counties?