Oracle: get X rows from point A - sql

I have a stored procedure which gets a list of items attached to a given configuration (each item is attached to one configuration ; if it's not (i.e. the field is NULL) then we consider it attaced to the default configuration (wich has its field ISDEFAULT to 1). I would like to get the items using pagination. I remembered that the ROWNUM field starts at one, so I wrote (using 0 for the first page, and ten items per page):
SELECT *
FROM
ITEMS item
WHERE
(ROWNUM - 1) >= 0 and
(ROWNUM - 1) < (0 + 10) and
(item.CONFIGID = 0 or
item.CONFIGID is null and exists (SELECT config.CONFIGID FROM CONFIGURATION config WHERE config.CONFIGID = 0 and config.ISDEFAULT = 1));
This request returned me zero results.
I changed it to:
SELECT item.NAME,
item.ITEMID,
item.CONFIGID,
ROWNUM
FROM
ITEMS item
WHERE
(ROWNUM - 1) >= 0 and
(ROWNUM - 1) < (0 + 10) and
(item.CONFIGID = 0 or
item.CONFIGID is null and exists (SELECT config.CONFIGID FROM CONFIGURATION config WHERE config.CONFIGID = 0 and config.ISDEFAULT = 1));
And it worked! Adding the ROWNUM in the SELECT statement fixed the issue.
Then I tried:
SELECT *
FROM
ITEMS item
WHERE
ROWNUM >= (0 + 1) and
ROWNUM < (0 + 1 + 10) and
(item.CONFIGID = 0 or
item.CONFIGID is null and exists (SELECT config.CONFIGID FROM CONFIGURATION config WHERE config.CONFIGID = 0 and config.ISDEFAULT = 1));
I wrote back the SELECT * statement and moved the - 1 to the other sides of the comparison operators (therefore they became + 1). This works as well.
Can someone explain why my first query does not work and yields zero results whereas the other two work like a charm?

I think there is nothing wrong with ROWNUM.
Can you try to execute:
SELECT *
FROM ITEMS item
WHERE
(ROWNUM - 1) >= 0 and
(ROWNUM - 1) < (0 + 10);
SELECT item.NAME,
item.ITEMID,
item.CONFIGID,
ROWNUM
FROM ITEMS item
WHERE
(ROWNUM - 1) >= 0 and
(ROWNUM - 1) < (0 + 10);
Both should work ok. Which means you have problem with second part of the filter.
The following subquery
(SELECT config.CONFIGID FROM CONFIGURATION config WHERE config.CONFIGID = 0 and config.ISDEFAULT = 1));
is uncorreleated. So it whould always return same result for any record. I think you have to add brackets into condition by item.CONFIGID:
(item.CONFIGID = 0) or
(
item.CONFIGID is null and
exists (SELECT ...)
)
or
(item.CONFIGID = 0 or
item.CONFIGID is null) and
exists (SELECT ...)
I think that is your problem. But not sure without seeing data.

Related

Subquery Building a Column

I am wondering if there is a more efficient way of doing the following query.
SELECT
t.iCertification_UserCertificationTranscriptID AS I_CERT_TRANSCRIPT_DIM,
t.iHR_UserID AS I_EMP_DIM,
t.iCertification_CertificationID AS I_RUL_CERT_DIM,
(
SELECT iHR_PositionID
FROM UserProfileNoGroups AS prof
WHERE
(iHR_UserID = t.iHR_UserID)
AND (Profile_IsPrimary = '1')
AND (UserPosition_IsPrimary = '1')
AND (t.dEffectiveStart BETWEEN UserPosition_Effective AND COALESCE(UserPosition_End, LOCALTIMESTAMP))
ORDER BY UserPosition_Effective DESC
FETCH FIRST 1 ROWS ONLY
) AS I_POSN_DIM,
CAST(EXTRACT(YEAR FROM t.dEffectiveStart) * 10000 + EXTRACT(MONTH FROM t.dEffectiveStart) * 100 + EXTRACT(DAY FROM t.dEffectiveStart) AS INT) AS I_DAY_STRT_DIM,
CAST(EXTRACT(YEAR FROM t.dEffectiveEnd) * 10000 + EXTRACT(MONTH FROM t.dEffectiveEnd) * 100 + EXTRACT(DAY FROM t.dEffectiveEnd) AS INT) AS I_DAY_END_DIM,
CASE
WHEN t.iCertification_TranscriptSourceID = '1' THEN 'Y'
ELSE 'N'
END AS L_AdminOverride,
t.mComment AS X_Comment
FROM
dbo.tCertification_UCT AS t;
I am concerned of the query that builds column I_POSN_DIM. The query performs slowly and it seems like that query has to be scanned over and over to extract results. This is in postgresql and I am not that familiar with this platform. I tried using CTEs that uses the MAX function, but the COUNT always turns out different from this query. Any assistance would be appreaciated.
I thought something like this:
SELECT
t.iCertification_UserCertificationTranscriptID AS I_CERT_TRANSCRIPT_DIM,
t.iHR_UserID AS I_EMP_DIM,
t.iCertification_CertificationID AS I_RUL_CERT_DIM,
uPG.iHR_PositionID AS I_POSN_DIM,
(t.dEffectiveStart.Year * 10000 + t.dEffectiveStart.Month * 100 + t.dEffectiveStart.Day) AS I_DAY_STRT_DIM,
(t.dEffectiveEnd.Year * 10000 + t.dEffectiveEnd.Month * 100 + t.dEffectiveEnd.Day) AS I_DAY_END_DIM,
CASE WHEN t.iCertification_TranscriptSourceID = '1' THEN 'Y' ELSE 'N' END AS L_AdminOverride,
t.mComment AS X_Comment
FROM dbo.tCertification_UCT AS t
INNER JOIN UserProfileNoGroups uPG ON t.iHR_UserID = uPG.iHR_UserID
WHERE Profile_IsPrimary = '1'
AND UserPosition_IsPrimary = '1'
AND (t.dEffectiveStart BETWEEN UserPosition_Effective AND COALESCE(UserPosition_End, LOCALTIMESTAMP));
I moved the inner query to outside and I convert it in an equivalent inner join. In others fields you did cast but it's not neccesary because of the result from that operations is an int.
But I supossed that table UserProfileNoGroups has 1 record for each record at tCertification_UCT. If it's not this way, the query would change a litlle:
SELECT
t.iCertification_UserCertificationTranscriptID AS I_CERT_TRANSCRIPT_DIM,
t.iHR_UserID AS I_EMP_DIM,
t.iCertification_CertificationID AS I_RUL_CERT_DIM,
uPG.iHR_PositionID OVER (ORDER BY UserPosition_Effective DESC) AS I_POSN_DIM,
(t.dEffectiveStart.Year * 10000 + t.dEffectiveStart.Month * 100 + t.dEffectiveStart.Day) AS I_DAY_STRT_DIM,
(t.dEffectiveEnd.Year * 10000 + t.dEffectiveEnd.Month * 100 + t.dEffectiveEnd.Day) AS I_DAY_END_DIM,
CASE WHEN t.iCertification_TranscriptSourceID = '1' THEN 'Y' ELSE 'N' END AS L_AdminOverride,
t.mComment AS X_Comment
FROM dbo.tCertification_UCT AS t
INNER JOIN UserProfileNoGroups uPG ON t.iHR_UserID = uPG.iHR_UserID
WHERE Profile_IsPrimary = '1'
AND UserPosition_IsPrimary = '1'
AND (t.dEffectiveStart BETWEEN UserPosition_Effective AND COALESCE(UserPosition_End, LOCALTIMESTAMP));
You can play with OVER clause:
https://www.postgresql.org/docs/9.1/tutorial-window.html

Custom row number in SQL

I am using a software which doesn't have a feature on custom row number. In order to resolve my issue, I have to tweak it on my SQL and create a custom column for the custom row number. Here is what I want to do as my custom row number along with my select statement:
li{
list-style-type: none;
}
<ul>
<li>1.</li>
<li>2.</li>
<li>2.1</li>
<li>2.2</li>
<li>2.3</li>
<li>3.</li>
<li>3.1</li>
<li>3.2</li>
</ul>
I am using Microsoft SQL Server. I tried to use variables but it doesn't work the way I wanted to be. Here is my sample faulty output:
SELECT chart.col1,CASE
WHEN chart.CHART_LEVEL <= 2 THEN #no += 1
WHEN chart.CHART_LEVEL => 3 THEN CONCAT(#no,'.'#subNo += 1)
ELSE ''
END
AS 'RowNo'
FROM myTable chart
I am really sorry for giving insufficient details.
If you have a column in that table, that defines an order, you could get the section numbers with subqueries fetching the count of rows with lower ordered rows with the respective level.
Assuming the column defining the order is id something looking like this could be what you want.
SELECT col1,
convert(varchar(max), (SELECT count(*)
FROM myTable chart1
WHERE chart1.id <= chart.id
AND chart1.chart_level <= 2))
+
CASE
WHEN (SELECT count(*)
FROM myTable chart2
WHERE chart2.id <= chart.id
AND chart2.chart_level > 2
AND (SELECT count(*)
FROM myTable chart3
WHERE chart3.id <= chart.id
AND chart3.chart_level <= 2)
= (SELECT count(*)
FROM myTable chart4
WHERE chart4.id <= chart2.id
AND chart4.chart_level <= 2)) = 0
THEN ''
ELSE
'.' + convert(varchar(max), (SELECT count(*)
FROM myTable chart2
WHERE chart2.id <= chart.id
AND chart2.chart_level > 2
AND (SELECT count(*)
FROM myTable chart3
WHERE chart3.id <= chart.id
AND chart3.chart_level <= 2)
= (SELECT count(*)
FROM myTable chart4
WHERE chart4.id <= chart2.id
AND chart4.chart_level <= 2)))
END RowNo
FROM myTable chart;

how to select in if statements

I want to test two TaskChangeLogIDs where ID '28' has a NewRollback of 0, and ID '31' has a NewRollback of 1.
I want make sure that depending which ID is called, it selects the correct NewRollback number. I get an error applying the code below in a new window so just need help tweaking this and learn on how to test things like this in the future. I'm getting an error in the IF statement line only.
SELECT isnull(NewRollback, 0) FROM Core.TaskChangeLog
WHERE TaskChangeLogID = 28
SELECT isnull(NewRollback, 0) FROM Core.TaskChangeLog
WHERE TaskChangeLogID = 31
IF (NewRollback = 1)
Select * from Core.TaskChangeLog where NewRollback = 1
ELSE
BEGIN
Select * from Core.TaskChangeLog where NewRollback = 0
END
You don't need IF condition to do this just AND/OR logic will do the job
Try this way
Select * from Core.TaskChangeLog
where (NewRollback = 1 and askChangeLogID = 28)
OR (NewRollback = 0 and askChangeLogID = 31)
To do some action based on select result try this
IF EXISTS (Select * from Core.TaskChangeLog
where NewRollback = 1 and askChangeLogID = 28) -- Replace with your ID
BEGIN
--some action
END
IF EXISTS (Select * from Core.TaskChangeLog
where NewRollback = 0 and askChangeLogID = 28)
BEGIN
--some action
END
How about putting all this in one query? This is my best guess as to the logic you want:
Select *
from Core.TaskChangeLog
where NewRollback = (SELECT COALESCE(NewRollback, 0)
FROM Core.TaskChangeLog
WHERE TaskChangeLogID = XX
);
Your code fails because the NewRollback in the IF is undefined. You could use a variable, but that would just complicate the code. And, the code should be simplified rather than made more complicated.

troubles with next and previous query

I have a list and the returned table looks like this. I took the preview of only one car but there are many more.
What I need to do now is check that the current KM value is larger then the previous and smaller then the next. If this is not the case I need to make a field called Trustworthy and should fill it with either 1 or 0 (true/ false).
The result that I have so far is this:
validKMstand and validkmstand2 are how I calculate it. It did not work in one list so that is why I separated it.
In both of my tries my code does not work.
Here is the code that I have so far.
FullList as (
SELECT
*
FROM
eMK_Mileage as Mileage
)
, ValidChecked1 as (
SELECT
UL1.*,
CASE WHEN EXISTS(
SELECT TOP(1)UL2.*
FROM FullList AS UL2
WHERE
UL2.FK_CarID = UL1.FK_CarID AND
UL1.KM_Date > UL2.KM_Date AND
UL1.KM > UL2.KM
ORDER BY UL2.KM_Date DESC
)
THEN 1
ELSE 0
END AS validkmstand
FROM FullList as UL1
)
, ValidChecked2 as (
SELECT
List1.*,
(CASE WHEN List1.KM > ulprev.KM
THEN 1
ELSE 0
END
) AS validkmstand2
FROM ValidChecked1 as List1 outer apply
(SELECT TOP(1)UL3.*
FROM ValidChecked1 AS UL3
WHERE
UL3.FK_CarID = List1.FK_CarID AND
UL3.KM_Date <= List1.KM_Date AND
List1.KM > UL3.KM
ORDER BY UL3.KM_Date DESC) ulprev
)
SELECT * FROM ValidChecked2 order by FK_CarID, KM_Date
Maybe something like this is what you are looking for?
;with data as
(
select *, rn = row_number() over (partition by fk_carid order by km_date)
from eMK_Mileage
)
select
d.FK_CarID, d.KM, d.KM_Date,
valid =
case
when (d.KM > d_prev.KM /* or d_prev.KM is null */)
and (d.KM < d_next.KM /* or d_next.KM is null */)
then 1 else 0
end
from data d
left join data d_prev on d.FK_CarID = d_prev.FK_CarID and d_prev.rn = d.rn - 1
left join data d_next on d.FK_CarID = d_next.FK_CarID and d_next.rn = d.rn + 1
order by d.FK_CarID, d.KM_Date
With SQL Server versions 2012+ you could have used the lag() and lead() analytical functions to access the previous/next rows, but in versions before you can accomplish the same thing by numbering rows within partitions of the set. There are other ways too, like using correlated subqueries.
I left a couple of conditions commented out that deal with the first and last rows for every car - maybe those should be considered valid is they fulfill only one part of the comparison (since the previous/next rows are null)?

how to limit a sql integer query result to <=1

how to limit an integer query result to 1. a return of 2 to be 1, a return 1 to be 1, and a return of 0.5 to be 0.5 because it is <= 1. i don't want to modify the tables, i just want to modify the results.
This is my exact query.
select ((select "V01" from sports where "UID" = '1') * 1.0 ) /
(select "V01" from master where "BALL" = 'REQUIREMENT') ;
I'm using postgres.
To limit, you'd do something like this:
select
case
when yourNumber >= 1 then 1
else yourNumber
end
...
Then you just apply this concept to your query.
As noted by Wiseguy, you could also do:
select LEAST(yourNumber, 1)
, since this is postgresql.
The first solution will work with any ANSI SQL compatible database.
Update
Applied to your query, I think (if I understood what you want correctly) it would be like this:
select LEAST(1,
((select "V01" from sports where "UID" = '1') * 1.0 ) /
(select "V01" from master where "BALL" = 'REQUIREMENT')
);
use the LEAST function , docs: http://www.postgresql.org/docs/8.3/static/functions-conditional.html. Also, check out GREATEST too
SELECT LEAST(1, <your value>)
EDIT replaced GREATEST with LEAST
try this:
select CASE
WHEN ((select V01 from sports where UID = '1') * 1.0 ) /
(select V01 from master where BALL = 'REQUIREMENT') >= 1
THEN 1
ELSE ((select V01 from sports where UID = '1') * 1.0 ) /
(select V01 from master where BALL = 'REQUIREMENT')
END;