I am a SQL server guy, with limited Oracle coding. I have inherited a bunch of views that I need to convert to ANSI. What I am looking for is someone to educate me, if possible, on a systematic way to do this in steps. I have junior guys that would read and understand TSQL style code, instead of the way it is. I want to take this knowledge, and have them start working on them; there are many. A conversion tool will not work, we don't have time to play with one, no budget to buy it, and our IT department will not let us install anything. (We have to do this manually)
I would like to know the steps (systematic approach) based on interpreting the code, not learn the data or try to understand why the existing code was written the way it was.
For example, are these the steps:
Reorder the tables to match how they are in the where clause.
Separate the lines in the where clause based on their table, so that it is easier to read.
Replace the Oracle join operators with left, right, outer, etc.
Leave the lines with defined values in the where clause, or should I include them with an "AND" in the table join.
This task will be done by junior SQL server guys, so it has to be as simple and straight forward as possible.
FROM WORK MECHANISMS WM,
WT_MANPOWER_RESOURCES WTMR,
LOGICAL_ITEMS LI,
APSWHS.WMX_LOG_REL_ELEM WLRE,
WMECH_DSGN_COMP_ELEMENTS WDCE,
WORK_TASKS WT,
persons_v per,
APSWHS.WMX_WO_WF_STATUS WFA,
RT_DETAILS RTD
WHERE
WFA.WF_SEQ > 639
AND WTMR.WTASK_WMECH_DB_ID = WM.DB_ID
AND WTMR.WTASK_DB_ID = 0
AND WTMR.WTMANPOW_TYPE = ‘LEAD’
AND WT.WMECH_DB_ID = WM.DB_ID
AND WT. DB_ID= 0
AND WFA.WM_DB_ID = WM.DB_ID
AND PER.per_db_id = wm.assigned_to_per_db_id
AND RTD.WMECH_DB_ID = WM.DB_ID
AND WDCE .WMECH_DB_ID = WFA.WM_DB_ID
AND WDCE.LITM_ID = WLRE.LITM_ID
AND WDCE.LITM_ID = LI.ID
AND WDCE.PRIMARY_DCID_FLAG = ‘Y’
I'm assuming that you want to migrate from table lists (join predicates in WHERE clause) to ANSI JOIN syntax. This has nothing to do with T-SQL.
Ideally, you'll take an ERD and check in what order the tables should be listed, visually. I personally find that easier than from mere text. Although, it is possible to do with plain text as well. Here are the steps:
Take the first table and all its non-join predicates:
FROM WORK_MECHANISMS WM
WHERE 1 = 1 -- No predicates on this table
Take the next table that you can join to the first one, and all of its join and non-join predicates
FROM WORK_MECHANISMS WM
JOIN WT_MANPOWER_RESOURCES WTMR
ON WTMR.WTASK_WMECH_DB_ID = WM.DB_ID
WHERE WTMR.WTASK_DB_ID = 0
AND WTRM.WTMANPOW_TYPE = 'LEAD'
And the next table...
Observe that this isn't the next table in your original table list, but another one, i.e. the next one that can be joined to the existing join graph without creating a cartesian product. In particular, I skipped (for now) LOGICAL_ITEMS and APSWHS.WMX_LOG_REL_ELEM and WMECH_DSGN_COMP_ELEMENTS. I will add them to the graph later.
FROM WORK_MECHANISMS WM
JOIN WT_MANPOWER_RESOURCES WTMR
ON WTMR.WTASK_WMECH_DB_ID = WM.DB_ID
JOIN WORK_TASKS WT
ON WT.WMECH_DB_ID = WM.DB_ID
WHERE WTMR.WTASK_DB_ID = 0
AND WTRM.WTMANPOW_TYPE = 'LEAD'
AND WT.DB_ID = 0
And the next table...
You continue adding tables to your new statement, until you've added all tables. If you ever encounter a (+) operator, "just" get the LEFT JOIN semantics right.
Related
I have the following SQL-code in my (SAP IdM) Application:
Select mcmskeyvalue as MKV,v1.searchvalue as STARTDATE, v2.avalue as Running_Changes_flag
from idmv_entry_simple
inner join idmv_value_basic_active v1 on mskey = mcmskey and attrname = 'Start_of_company_change'
and mcentrytype = 'MX_PERSON' and to_date(v1.searchvalue,'YYYY-MM-DD')<= sysdate+3
left join idmv_value_basic v2 on v2.mskey = mcmskey and v2.attrname = 'Running_Changes_flag'
where mcmskey not in (Select mskey from idmv_value_basic_active where attrname = 'Company_change_running_flag')
I already found the solution for the ORA-01841 problem, as it could either be a solution similar to MSSQLs try_to_date as mentioned here: How to handle to_date exceptions in a SELECT statment to ignore those rows?
or a solution where I change the code to something like this, to work soly on strings:
Select mcmskeyvalue as MKV,v1.searchvalue as STARTDATE, v2.avalue as Running_Changes_flag
from idmv_entry_simple
inner join idmv_value_basic_active v1 on mskey = mcmskey and attrname = 'Start_of_company_change'
and mcentrytype = 'MX_PERSON' and v1.searchvalue<= to_char(sysdate+3,'YYYY-MM-DD')
left join idmv_value_basic v2 on v2.mskey = mcmskey and v2.attrname = 'Running_Changes_flag'
where mcmskey not in (Select mskey from idmv_value_basic_active where attrname = 'Company_change_running_flag')
So for the actually problem I have a solution.
But now I came into discussion with my customers and teammates why the error happens at all.
Basically for all entries of idmv_value_basic_activ that comply to the requirement of "attrname = 'Start_of_company_change'" we can be sure that those are dates. In addition, if we execute the query to check all values that would be delivered, all are in a valid format.
I learned in university that the DB-Engine could decide in which order it will run individual segments of a query. So for me the most logical explanation would be that, on the development environment (where we face the problem), the section " to_date(v1.searchvalue,'YYYY-MM-DD')<= sysdate+3” is executed before the section “attrname = 'Start_of_company_change'”
Whereas on the productive environment, where everything works like a charm, the segments are executed in the order that is descripted by the SQL Statement.
Now my Question is:
First: do I remember that right, since the teacher said that only once and at that time I could not really make sense out of it
And Second: Is this assumption of mine correct or is there another reason for the problem?
Borderinformation:
The Tool uses a kind of shifted data structure which is why there can be quite a few different types in the actual “Searchvalue” column of the idmv_value_basic_activ view. The datatype on the database layer is always a varchar one.
"the DB-Engine could decide in which order it will run individual segments of a query"
This is correct. A SQL query is just a description of the data you want and where it's stored. Oracle will calculate an execution plan to retrieve that data as best it can. That plan will vary based on any number of factors, like the number of actual rows in the table and the presence of indexes, so it will vary from environment to environment.
So it sounds like you have an invalid date somewhere in your table, so to_date raises an exception. You can use validate_conversion to find it.
I have this SQL query which is really complex to me and I tried to convert it to EntityFramework Core code based, but I couldn't do at least the multiple join.
SELECT vrCore_Product.iMasterId, vrCore_Product.sName[Particular],
SUM(tCore_Indta_0.fQuantityInBase) - ISNULL(AVG(tCore_ReservedStock_0.fQuantity),0)[Net Quantity],vrPos_Outlet.iMasterId[Product]
,vrCore_Product.sCode[vrCore_Product.sCode0] FROM tCore_Data_0
JOIN tCore_Header_0 ON tCore_Header_0.iHeaderId = tCore_Data_0.iHeaderId
JOIN tCore_Indta_0 ON tCore_Data_0.iBodyId = tCore_Indta_0.iBodyId
JOIN cCore_Vouchers_0 WITH (READUNCOMMITTED) ON tCore_Header_0.iVoucherType = cCore_Vouchers_0.iVoucherType
JOIN vrCore_Product ON vrCore_Product.iMasterId = tCore_Indta_0.iProduct AND vrCore_Product.iTreeId = 0
JOIN vrPos_Outlet ON vrPos_Outlet.iMasterId = tCore_Data_0.iInvTag
LEFT JOIN
(
Select iProduct, tCore_Data_0.iInvTag, SUM(case bReserveOrRelease when 0 then tCore_ReservedStock_0.fQuantity else -tCore_ReservedStock_0.fQuantity end) fQuantity
FROM tCore_ReservedStock_0
JOIN tCore_Data_0 ON tCore_Data_0.iTransactionId = tCore_ReservedStock_0.iTransactionId
JOIN tCore_Indta_0 ON tCore_Indta_0.iBodyId = tCore_Data_0.iBodyId
JOIN tCore_Header_0 ON tCore_Header_0.iHeaderId = tCore_Data_0.iHeaderId
WHERE tCore_Header_0.bSuspended = 0
GROUP BY iProduct,tCore_Data_0.iInvTag
HAVING SUM(CASE bReserveOrRelease WHEN 0 THEN tCore_ReservedStock_0.fQuantity ELSE -tCore_ReservedStock_0.fQuantity END)<>0
)tCore_ReservedStock_0 ON tCore_ReservedStock_0.iProduct = tCore_Indta_0.iProduct AND tCore_ReservedStock_0.iInvTag = tCore_Data_0.iInvTag WHERE tCore_Header_0.bUpdateStocks = 1 AND tCore_Data_0.bSuspendUpdateStocks <> 1
AND tCore_Header_0.bSuspended = 0 AND tCore_Data_0.iAuthStatus < 2
AND (tCore_Header_0.iDate BETWEEN dbo.DateToInt('2020-01-10') AND dbo.DateToInt('2020-01-22') OR (tCore_Header_0.iDate < dbo.DateToInt('2020-01-22') AND tCore_Header_0.iVoucherClass = 512)) AND vrCore_Product.iProductType <> 'Service' AND vrPos_Outlet.iMasterId IN (26) GROUP BY vrPos_Outlet.iMasterId, vrCore_Product.iMasterId, vrCore_Product.sName ,vrCore_Product.sCode HAVING SUM(tCore_Indta_0.fQuantity) <> 0 ORDER BY vrPos_Outlet.iMasterId
We think in objects when we code in C#. We think relational when we do T-SQL. This creates a problem called object-relational impedance mismatch. EF helps break that problem by allowing us to think only in objects. You are trying to do the exact opposite, which is translate a query back to an object representation, via a Linq query. Here's a tip: I never do that. My starting point is the C# object model and I model the query thinking in Linq. I don't care about T-SQL.
This might seem like just a matter of syntax, since both T-SQL and Linq are query languages, and in the end, Linq gets translated to T-SQL by EF. However, the difference between them is not only syntax, but the way we think. For instance, while we think about joins in T-SQL, in linq, we think about navigation properties. On one, we think about relations and foreign keys, on the other, we think about objects and graphs.
In the very rare case where I'm better off with a T-SQL query, I rather execute a raw query instead of going through the pain of translating it back to Linq: https://learn.microsoft.com/en-us/ef/core/querying/raw-sql
In your case I would do either of the following 2 things:
I would either simply throw away the T-SQL query and start over, thinking in Linq. It helps if I have a good understanding of the business domain and what I need to extract using that query. This is most of the time, favourite approach.
Or, in case I'm too committed to the query (pun not intended), I would simply execute it as a raw query using EF (always using parameters instead of concatenating the query with input values, in order to prevent any Sql Injection vulnerability). The only downside to this that your code stops being database engine independent the moment you add a raw query to it.
If you have relationships, you just can use Include() like
var data = _context.TcoreData0
.Include(x => x.TcoreHeader0)
.Include(x => x.TcoreIndta0)
.Include("tCore_Header_0.cCore_Vouchers_0")
.Include...
I am my subquery is severely slowing my full query down in MySQL. I'm in the process of converting the original query to work on MySQL as I'm moving away from SQL Server where it has worked wonderfully. MySQL on the other hand isnt too happy. Was wondering if anyone could assist in helping me with a conversion solution to a join as I'm not well versed in joins quite yet. Thanks!
select a.crm_ticket_details_detail,
crm_ticket_created_date,
crm_ticket_id,
crm_ticket_customer_id,
c.crm_assigned_user
from php_crm.crm_ticket,
php_crm.crm_ticket_details a,
php_crm.crm_assigned c
where crm_ticket_resolved_date is null
and crm_ticket_id = a.crm_ticket_details_ticket_id
and a.crm_ticket_details_type = 'issue'
and c.crm_assigned_ticket_id = crm_ticket_id
and c.crm_assigned_id = (select max(d.crm_assigned_id)
from php_crm.crm_assigned d
where d.crm_assigned_ticket_id = crm_ticket_id)
SELECT
details.crm_ticket_details_detail,
CT.crm_ticket_created_date,
CT.crm_ticket_id,
CT.crm_ticket_customer_id,
ASSIGNED.crm_assigned_user
FROM
php_crm.crm_ticket CT (NONAME)
INNER JOIN php_crm.crm_ticket_details DETAILS -- (A)
ON CT.crm_ticket_id = DETAILS.crm_ticket_details_ticket_id
INNER JOIN php_crm.crm_assigned ASSIGNED -- (C)
ON CT.crm_ticket_id = ASSIGNED.crm_assigned_ticket_id
WHERE
crm_ticket_resolved_date IS NULL
AND DETAILS.crm_ticket_details_type = 'issue'
AND
AND ASSIGNED.crm_assigned_id = (SELECT
max(d.crm_assigned_id)
FROM
php_crm.crm_assigned d
WHERE
d.crm_assigned_ticket_id = crm_ticket_id)
I believe that's what you're looking for. I can't speak to whether it will actually improve performance, although it will certainly make it easier to understand. I'm not sure the old style of joins is actually less efficient; just harder to read / easier to make product joins with.
That said, if there are other common keys between the three tables that are being indirectly neutralized in other parts of the logic then that could have a performance impact.
(EDIT: Actually not sure if this was what you're looking for, reread your question and you seem focused on the subquery... I don't see any problems jumping out with that, would need more details to address that.)
here are my tables, im using sql developer oracle
Carowner(Carowner id, carowner-name,)
Car (Carid, car-name, carowner-id*)
Driver(driver_licenceno, driver-name)
Race(Race no, race-name, prize-money, race-date)
RaceEntry(Race no*, Car id*, Driver_licenceno*, finishing_position)
im trying to list to do the query below
which drivers have come second in races from the start of this year.
lncluding race name, driver name, and the name of the car in the output
i have attempted
select r.racename, d.driver-name, c.carowner-name
from race r, driver d, car c, raceentry re
where re.finishing_position = 2 and r.race-date is ...
Something like:
select r.racename, d.driver-name, c.carowner-name
from race r
join raceentry re on r.race_no = re.race_no
join car c on re.car_Id = c.car_id
join driver d on re.driverliscenceNo = d.driverliscenceNo
where re.finishing_position = 2 and r.race-date >='20130101'
This assumes only one car and one driver with a finsih place of 2nd in a particular race. You may need more conditions otherwise. If this is your own table design, you need to start right now learning to be consistent in your nameing between tables. It is important. Fields that are in multiple tables should have the same name and data type. Also you need to stop using implicit syntax. This ia aSQL antipattern and a very poor programming technique. It leads to mistakes such as accidental cross joins and is harder to read and maintain when things get more complex. As you are clearly learning, you need to stop this bad habit right now.
First off, multiple joins in the where clause are hard to get used to when you define more than 3 or 4 tables IMHO.
Do this instead:
Select
a.columnfroma
, b.columnfromb
, c.columnfromc
from tablea a
join tableb b on a.columnAandBShare = b.columnAandBShare
join tablec c on b.columnBandCShare = c.columnBandCShare
This while no one would say is a method you have to use, it is a much more readable method of performing joins.
Otherwise you are doing the joins in the where clause and if you have other predicates with your joins you are going to have to comment out which is which if you ever need to go back and look at it.
I've got an Access MDB I use for reporting that has linked table views from SQL Server 2005. I built a query that retrieves information off of a PO table and categorizes the line item depending on information from another table. I'm relatively certain the query was fine until approximately a month ago when we shifted from compatibility mode 80 to 90 on the Server as required by our primary application (which creates the data). I can't say this with 100% certainty, but that is the only major change made in the past 90 days. We noticed that suddenly data was not showing up in the query making the reports look odd.
This is a copy of the failing query:
SELECT dbo_porel.jobnum, dbo_joboper.opcode, dbo_porel.jobseqtype,
dbo_opmaster.shortchar01,
dbo_porel.ponum, dbo_porel.poline, dbo_podetail.unitcost
FROM ((dbo_porel
LEFT JOIN dbo_joboper ON (dbo_porel.assemblyseq = dbo_joboper.assemblyseq)
AND (dbo_porel.jobseq = dbo_joboper.oprseq)
AND (dbo_porel.jobnum = dbo_joboper.jobnum))
LEFT JOIN dbo_opmaster ON dbo_joboper.opcode = dbo_opmaster.opcode)
LEFT JOIN dbo_podetail ON (dbo_porel.poline = dbo_podetail.poline)
AND (dbo_porel.ponum = dbo_podetail.ponum)
WHERE (dbo_porel.jobnum="367000003")
It returns the following:
jobnum opcode jobseqtype shortchar01 ponum poline unitcost
367000003 S 6624 2 15
The query normally should have displayed a value for opcode and shortchar01. If I remove the linked table dbo_podetail, it properly displays data for these fields (although I obviously don't have unitcost anymore). At first I thought it might be a data issue, but I found if I nested the query and then linked the table, it worked fine.
For example the following code works perfectly:
SELECT qryTest.*, dbo_podetail.unitcost
FROM (
SELECT dbo_porel.jobnum, dbo_joboper.opcode, dbo_porel.jobseqtype,
dbo_opmaster.shortchar01, dbo_porel.ponum, dbo_porel.poline
FROM (dbo_porel
LEFT JOIN dbo_joboper ON (dbo_porel.jobnum=dbo_joboper.jobnum)
AND (dbo_porel.jobseq=dbo_joboper.oprseq)
AND (dbo_porel.assemblyseq=dbo_joboper.assemblyseq))
LEFT JOIN dbo_opmaster ON dbo_joboper.opcode=dbo_opmaster.opcode
WHERE (dbo_porel.jobnum="367000003")
) As qryTest
LEFT JOIN dbo_podetail ON (qryTest.poline = dbo_podetail.poline)
AND (qryTest.ponum = dbo_podetail.ponum)
I'm at a loss for why it works in the latter case and not in the first case. Worse yet, it seems to work intermittently for some records and not for others (it's consistent about the ones it does and does not work for).
Do any of you experts have any ideas?
You definitely need to use subqueries for multiple left/right joins in Access.
I think it's a limitation of the Jet optimizer that gets confused if you're just chaining left/right joins.
You can see that this is a recurrent problem that surfaces often.
I'm always confused by Access' use of brackets in joins. Try stripping out the extra brackets.
FROM
dbo_porel
LEFT JOIN
dbo_joboper ON (dbo_porel.assemblyseq = dbo_joboper.assemblyseq)
AND (dbo_porel.jobseq = dbo_joboper.oprseq)
AND (dbo_porel.jobnum = dbo_joboper.jobnum)
LEFT JOIN
dbo_opmaster ON (dbo_joboper.opcode = dbo_opmaster.opcode)
LEFT JOIN
dbo_podetail ON (dbo_porel.poline = dbo_podetail.poline)
AND (dbo_porel.ponum = dbo_podetail.ponum)
OK the above doesn't work - Sorry I give up