Query with equation - sql

I have 3 queries that return 3 values. I'd Like to join the queries to perform the following expression:
(MgO + CaO)/SiO2
How can I do that?
MgO:
SELECT sampled_date, result
FROM AF_VW WHERE element = 'MgO' AND ROWNUM = 1 ORDER BY sampled_date DESC;
CaO:
SELECT sampled_date, result
FROM AF_VW WHERE element = 'CaO' AND ROWNUM = 1 ORDER BY sampled_date DESC;
SiO2:
SELECT sampled_date, result
FROM AF_VW WHERE element = 'SiO2' AND ROWNUM = 1 ORDER BY sampled_date DESC;

with x as (
SELECT sampled_date, result, element,
row_number() over(partition by element order by sampled_date desc) rn
FROM AF_VW)
, y as (
select
case when element = 'MgO' then result end as MGO,
case when element = 'CaO' then result end as CaO,
case when element = 'SiO2' then result end as SiO2
FROM x where rn = 1)
select (mgo+cao)/sio2 from y;
You can use row_number function instead of rownum and then select the results for the 3 elements.

This is a bit long for a comment.
The queries in your question are probably not doing what you expect. Oracle evaluates the WHERE clause before the order by. So, the following chooses one arbitrary row with MgO and then does the trivial ordering of the one row by date:
SELECT sampled_date, result
FROM AF_VW
WHERE element = 'MgO' AND ROWNUM = 1
ORDER BY sampled_date DESC;
Really, to get the equivalent result, you would need to emulate the same, unstable logic. Unstable, because the results are not guaranteed to be the same if the query is run multiple times:
with mg as (
SELECT sampled_date, result
FROM AF_VW
WHERE element = 'MgO' AND ROWNUM = 1
),
coa as (
SELECT sampled_date, result
FROM AF_VW
WHERE element = 'CaO' AND ROWNUM = 1
),
sio2 as (
SELECT sampled_date, result
FROM AF_VW
WHERE element = 'SiO2' AND ROWNUM = 1
)
select (mgo.result + cao.result) / sio2.result
from mgo cross join cao cross join sio2;
I suspect you really want the most recent sample date, which is what VKP's answer provides. I just thought you should know that is not what your current queries are doing.

Related

"ORA-00923: FROM keyword not found where expected\n what should I fix

I have an oracle query as follows but when I make changes to pagination the results are different. what should i pass for my code
SELECT *
FROM (
SELECT b.*,
ROWNUM r__
FROM (
select a.KODE_KLAIM,
a.NO_SS,
a.LA,
a.NAMA_TK,
a.KODE_K,
(
select tk.TEM_LAHIR
from KN.VW_KN_TK tk
where tk.KODE_K = a.KODE_K and rownum=1
) TEM_LAHIR,
(
select TO_CHAR(tk.TLAHIR, 'DD/MM/RRRR')
from KN.VW_KTK tk
where tk.KODE_K = a.KODE_K
and rownum=1
) TLAHIR
from PN.KLAIM a
where nvl(a.STATUS_BATAL,'X') = 'T'
and A.NOMOR IS NOT NULL
and A.TIPE_KLAIM = 'JPN01'
)b
)
where 1 = 1
WHERE ROWNUM < ( ( ? * ? ) + 1 )
WHERE r__ >= ( ( ( ? - 1 ) * ? ) + 1 )
but i run this query i have result ORA-00900: invalid SQL statement
You have three WHERE clauses at the end (and no ORDER BY clause). To make it syntactically valid you could change the second and third WHERE clauses to AND.
However, you mention pagination so what you probably want is to use:
SELECT *
FROM (
SELECT b.*,
ROWNUM r__
FROM (
select ...
from ...
ORDER BY something
)b
WHERE ROWNUM < :page_size * :page_number + 1
)
WHERE r__ >= ( :page_number - 1 ) * :page_size + 1
Note: You can replace the named bind variables with anonymous bind variables if you want.
Or, if you are using Oracle 12 or later then you can use the OFFSET x ROWS FETCH FIRST y ROWS ONLY syntax:
select ...
from ...
ORDER BY something
OFFSET (:page_number - 1) * :page_size ROWS
FETCH FIRST :page_size ROWS ONLY;
Additionally, you have several correlated sub-queries such as:
select tk.TEM_LAHIR
from KN.VW_KN_TK tk
where tk.KODE_K = a.KODE_K and rownum=1
This will find the first matching row that the SQL engine happens to read from the datafile and is effectively finding a random row. If you want a specific row then you need an ORDER BY clause and you need to filter using ROWNUM AFTER the ORDER BY clause has been applied.
From Oracle 12, the correlated sub-query would be:
select tk.TEM_LAHIR
from KN.VW_KN_TK tk
where tk.KODE_K = a.KODE_K
ORDER BY something
FETCH FIRST ROW ONLY

Select by length of characters

I have to select the longest phrase that has points>0 but being contained in a phrase which has points=0, if you look at the demo than the rows in output would be number 3 and 6:
http://sqlfiddle.com/#!18/e954f/1/0
many thanks in advance.
You can use a CTE to find all phrases with positive points which are a substring of a phrase with 0 points. Then you can find the maximum length of the substrings associated with each 0 point phrase, and JOIN that back to the CTE to get the phrase that matches that condition:
WITH cte AS (
SELECT w1.*, w2.id AS w2_id
FROM words w1
JOIN (SELECT *
FROM words
WHERE points = 0) w2 ON w1.phrase = LEFT(w2.phrase, LEN(w1.phrase))
WHERE w1.points > 0
)
SELECT cte.id, cte.phrase, points
FROM cte
JOIN (SELECT w2_id, MAX(LEN(phrase)) AS max_len
FROM cte
GROUP BY w2_id) cte_max ON cte_max.w2_id = cte.w2_id AND cte_max.max_len = LEN(cte.phrase)
Output:
id phrase points
3 tool box online 1
6 stone road 1
Updated SQLFiddle
You can use an inner join comparing the phrases with a LIKE to get only the ones contained in another phrase. Filter for the point in a WHERE clause. Then get the rank() partitioned by the phrase from the joined instance and ordered by the length descending. In an outer SELECT only get the ones with a rank of one.
SELECT x.id,
x.phrase,
x.points
FROM (SELECT w1.id,
w1.phrase,
w1.points,
rank() OVER (PARTITION BY w2.phrase
ORDER BY len(w1.phrase) DESC) r
FROM words w1
INNER JOIN words w2
ON w2.phrase LIKE concat(w1.phrase, '%')
WHERE w2.points = 0
AND w1.points > 0) x
WHERE x.r = 1;
SQL Fiddle
Edit:
To include the other phrase:
SELECT x.id,
x.phrase,
x.other_phrase,
x.points
FROM (SELECT w1.id,
w1.phrase,
w2.phrase other_phrase,
w1.points,
rank() OVER (PARTITION BY w2.phrase
ORDER BY len(w1.phrase) DESC) r
FROM words w1
INNER JOIN words w2
ON w2.phrase LIKE concat(w1.phrase, '%')
WHERE w2.points = 0
AND w1.points > 0) x
WHERE x.r = 1;
You will get from max to min length of phrase where points>0
SELECT *, LEN(phrase) AS Lenght FROM words where points>0 ORDER BY LEN(phrase) DESC
And if you want the longest phrase
SELECT TOP 1 *, LEN(phrase) AS Lenght FROM words where points>0 ORDER BY LEN(phrase) DESC

ROW_NUMBER() Query Plan SORT Optimization

The query below accesses the Votes table that contains over 30 million rows. The result set is then selected from using WHERE n = 1. In the query plan, the SORT operation in the ROW_NUMBER() windowed function is 95% of the query's cost and it is taking over 6 minutes to complete execution.
I already have an index on same_voter, eid, country include vid, nid, sid, vote, time_stamp, new to cover the where clause.
Is the most efficient way to correct this to add an index on vid, nid, sid, new DESC, time_stamp DESC or is there an alternative to using the ROW_NUMBER() function for this to achieve the same results in a more efficient manner?
SELECT v.vid, v.nid, v.sid, v.vote, v.time_stamp, v.new, v.eid,
ROW_NUMBER() OVER (
PARTITION BY v.vid, v.nid, v.sid ORDER BY v.new DESC, v.time_stamp DESC) AS n
FROM dbo.Votes v
WHERE v.same_voter <> 1
AND v.eid <= #EId
AND v.eid > (#EId - 5)
AND v.country = #Country
One possible alternative to using ROW_NUMBER():
SELECT
V.vid,
V.nid,
V.sid,
V.vote,
V.time_stamp,
V.new,
V.eid
FROM
dbo.Votes V
LEFT OUTER JOIN dbo.Votes V2 ON
V2.vid = V.vid AND
V2.nid = V.nid AND
V2.sid = V.sid AND
V2.same_voter <> 1 AND
V2.eid <= #EId AND
V2.eid > (#EId - 5) AND
V2.country = #Country AND
(V2.new > V.new OR (V2.new = V.new AND V2.time_stamp > V.time_stamp))
WHERE
V.same_voter <> 1 AND
V.eid <= #EId AND
V.eid > (#EId - 5) AND
V.country = #Country AND
V2.vid IS NULL
The query basically says to get all rows matching your criteria, then join to any other rows that match the same criteria, but which would be ranked higher for the partition based on the new and time_stamp columns. If none are found then this must be the row that you want (it's ranked highest) and if none are found that means that V2.vid will be NULL. I'm assuming that vid otherwise can never be NULL. If it's a NULLable column in your table then you'll need to adjust that last line of the query.

SELECTing only one copy of a row with a specific key that is coming from multiple tables

I am new to SQL so bear with me. I am returning data from multiple tables. Followed is my SQL (let me know if there is a better approach):
SELECT [NonScrumStory].[IncidentNumber], [NonScrumStory].[Description], [DailyTaskHours].[ActivityDate], [Application].[AppName], [SupportCatagory].[Catagory], [DailyTaskHours].[PK_DailyTaskHours],n [NonScrumStory].[PK_NonScrumStory]
FROM [NonScrumStory], [DailyTaskHours], [Application], [SupportCatagory]
WHERE ([NonScrumStory].[UserId] = 26)
AND ([NonScrumStory].[PK_NonScrumStory] = [DailyTaskHours].[NonScrumStoryId])
AND ([NonScrumStory].[CatagoryId] = [SupportCatagory].[PK_SupportCatagory])
AND ([NonScrumStory].[ApplicationId] = [Application].[PK_Application])
AND ([NonScrumStory].[Deleted] != 1)
AND [DailyTaskHours].[ActivityDate] >= '1/1/1990'
ORDER BY [DailyTaskHours].[ActivityDate] DESC
This is what is being returned:
This is nearly correct. I only want it to return one copy of PK_NonScrumStory though and I can't figure out how. Essentially, I only want it to return one copy so one of the top two rows would not be returned.
You could group by the NonScrumStore columns, and then aggregate the other columns like this:
SELECT [NonScrumStory].[IncidentNumber],
[NonScrumStory].[Description],
MAX( [DailyTaskHours].[ActivityDate]),
MAX( [Application].[AppName]),
MAX([SupportCatagory].[Catagory]),
MAX([DailyTaskHours].[PK_DailyTaskHours]),
[NonScrumStory].[PK_NonScrumStory]
FROM [NonScrumStory],
[DailyTaskHours],
[Application],
[SupportCatagory]
WHERE ([NonScrumStory].[UserId] = 26)
AND ([NonScrumStory].[PK_NonScrumStory] = [DailyTaskHours].[NonScrumStoryId])
AND ([NonScrumStory].[CatagoryId] = [SupportCatagory].[PK_SupportCatagory])
AND ([NonScrumStory].[ApplicationId] = [Application].[PK_Application])
AND ([NonScrumStory].[Deleted] != 1)
AND [DailyTaskHours].[ActivityDate] >= '1/1/1990'
group by [NonScrumStory].[IncidentNumber], [NonScrumStory].[Description],[NonScrumStory].[PK_NonScrumStory]
ORDER BY 3 DESC
From the screenshot it seems DISTINCT should have solved your issue but if not you could use the ROW_NUMBER function.
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY [NonScrumStory].[PK_NonScrumStory] ORDER BY [DailyTaskHours].[ActivityDate] DESC) AS RowNum,
[NonScrumStory].[IncidentNumber], [NonScrumStory].[Description], [DailyTaskHours].[ActivityDate], [Application].[AppName], [SupportCatagory].[Catagory], [DailyTaskHours].[PK_DailyTaskHours],n [NonScrumStory].[PK_NonScrumStory]
FROM [NonScrumStory], [DailyTaskHours], [Application], [SupportCatagory]
WHERE ([NonScrumStory].[UserId] = 26)
AND ([NonScrumStory].[PK_NonScrumStory] = [DailyTaskHours].[NonScrumStoryId])
AND ([NonScrumStory].[CatagoryId] = [SupportCatagory].[PK_SupportCatagory])
AND ([NonScrumStory].[ApplicationId] = [Application].[PK_Application])
AND ([NonScrumStory].[Deleted] != 1)
AND [DailyTaskHours].[ActivityDate] >= '1/1/1990'
)
SELECT * FROM CTE WHERE RowNum = 1 ORDER BY [ActivityDate] DESC
I believe if you add DISTINCT to your query that should solve your problem. Like so
SELECT DISTINCT [NonScrumStory].[IncidentNumber], [NonScrumStory].[Description],...

Fastest way to check if the the most recent result for a patient has a certain value

Mssql < 2005
I have a complex database with lots of tables, but for now only the patient table and the measurements table matter.
What I need is the number of patient where the most recent value of 'code' matches a certain value. Also, datemeasurement has to be after '2012-04-01'. I have fixed this in two different ways:
SELECT
COUNT(P.patid)
FROM T_Patients P
WHERE P.patid IN (SELECT patid
FROM T_Measurements M WHERE (M.code ='xxxx' AND result= 'xx')
AND datemeasurement =
(SELECT MAX(datemeasurement) FROM T_Measurements
WHERE datemeasurement > '2012-01-04' AND patid = M.patid
GROUP BY patid
GROUP by patid)
AND:
SELECT
COUNT(P.patid)
FROM T_Patient P
WHERE 1 = (SELECT TOP 1 case when result = 'xx' then 1 else 0 end
FROM T_Measurements M
WHERE (M.code ='xxxx') AND datemeasurement > '2012-01-04' AND patid = P.patid
ORDER by datemeasurement DESC
)
This works just fine, but it makes the query incredibly slow because it has to join the outer table on the subquery (if you know what I mean). The query takes 10 seconds without the most recent check, and 3 minutes with the most recent check.
I'm pretty sure this can be done a lot more efficient, so please enlighten me if you will :).
I tried implementing HAVING datemeasurment=MAX(datemeasurement) but that keeps throwing errors at me.
So my approach would be to write a query just getting all the last patient results since 01-04-2012, and then filtering that for your codes and results. So something like
select
count(1)
from
T_Measurements M
inner join (
SELECT PATID, MAX(datemeasurement) as lastMeasuredDate from
T_Measurements M
where datemeasurement > '01-04-2012'
group by patID
) lastMeasurements
on lastMeasurements.lastmeasuredDate = M.datemeasurement
and lastMeasurements.PatID = M.PatID
where
M.Code = 'Xxxx' and M.result = 'XX'
The fastest way may be to use row_number():
SELECT COUNT(m.patid)
from (select m.*,
ROW_NUMBER() over (partition by patid order by datemeasurement desc) as seqnum
FROM T_Measurements m
where datemeasurement > '2012-01-04'
) m
where seqnum = 1 and code = 'XXX' and result = 'xx'
Row_number() enumerates the records for each patient, so the most recent gets a value of 1. The result is just a selection.