Error when adding order by - sql

Goodday, Please check my query first.
SELECT *
FROM
(
SELECT DISTINCT row, a.tanggal, b.OutletCode, c.Nilai, a.Nip, b.Fullname,
a.KodePenilaian, f.Description AS posisilama, d.ShortDesc AS posisibaru
FROM penilaian_header a
LEFT JOIN Employee b
ON a.Nip = b.Nip
LEFT JOIN Position f
ON b.PositionCode = f.PositionCode
LEFT JOIN Position d
ON a.PositionCode = d.PositionCode
LEFT JOIN arealeader g
ON g.OutletCode = b.OutletCode
LEFT JOIN
(
SELECT ROW_NUMBER() OVER (PARTITION BY KodePenilaianH
ORDER BY idPenilaiand DESC) AS Row,
Nilai, KodePenilaianH
FROM penilaian_Detail
) c
ON a.KodePenilaian = c.KodePenilaianH
WHERE a.Outlet LIKE '%%' AND Periode LIKE '%%'
ORDER BY b.OutletCode ASC
) nilai PIVOT (SUM(nilai) FOR ROW IN ([1],[2],[3],[4],[5])) piv;
My problem is when i'm add Order by my query error. Here is the error :
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.
Without Order By my query working fine.

I believe that ORDER BY b.OutletCode ASC is the cause of the error, rather than the ORDER BY inside the partition, which is necessary and should be allowed.
If you want to return all records, you can use TOP with a large number, e.g.
SELECT *
FROM
(
SELECT DISTINCT TOP 2147483647 row, a.tanggal, b.OutletCode, c.Nilai, a.Nip,
b.Fullname, a.KodePenilaian, f.Description AS posisilama, d.ShortDesc AS posisibaru
FROM penilaian_header a
LEFT JOIN Employee b
ON a.Nip = b.Nip
...
ORDER BY b.OutletCode ASC
) nilai PIVOT (SUM(nilai) FOR ROW IN ([1],[2],[3],[4],[5])) piv;

Related

Left outer join and necessary subquery with date in Oracle

I'm struggling with a left outer join where the right portion, the one that could contain nulls, doesn't show me values. I think the situation is called an implicit inner join. The reason is that the subquery doesn't return nulls but rather nothing for what I'd like to have on my right part.
(This question differs from Left Outer Join Not Working? in that a subquery to find the maximum date is needed.)
Here is a simplified example:
Table Contracts:
customer_id, status
Table Values:
customer_id, value_code, value, begin_date
I want to display all customers with status = 'active' and the latest value for a certain value_code, lets say 'volume'. There are more value_codes and the values have a certain date from which they are valid. There can also be no value_code BUT then I want a NULL on the right side.
So here is what I tried to do:
SELECT * FROM CONTRACTS C
LEFT JOIN VALUES V ON C.CUSTOMER_ID = V.CUSTOMER_ID
AND VALUE_CODE = 'VOLUME'
WHERE C.STATUS = 'ACTIVE'
AND V.BEGIN_DATE = (
SELECT MAX(BEGIN_DATE) FROM VALUES V2
WHERE V2.CUSTOMER_ID = V.CUSTOMER_ID
AND V2.VALUE_CODE = 'VOLUME'
)
I can't put the subquery in the join clause, Oracle won't accept that. On the other hand my subquery makes it so that all the rows that have no entry for a value with code 'volume' are omitted. What I want is to have value = NULL instead with all the customers on the left.
Thanks for helping!
Filter the VALUE rows first and then LEFT JOIN:
SELECT *
FROM CONTRACTS C
LEFT JOIN
( SELECT *
FROM (
SELECT V.*,
ROW_NUMBER() OVER ( PARTITION BY CUSTOMER_ID ORDER BY BEGIN_DATE DESC )
AS rn
FROM VALUES V
)
WHERE rn = 1
) V
ON ( C.CUSTOMER_ID = V.CUSTOMER_ID
AND VALUE_CODE = 'VOLUME' )
WHERE C.STATUS = 'ACTIVE'
As MT0 suggested using partitioning and sorting by row number helped. Only had to include value_code in the partitioning for my purpose.
So this query finally did what I wanted:
SELECT *
FROM CONTRACTS C
LEFT JOIN
( SELECT *
FROM (
SELECT V.*,
ROW_NUMBER() OVER ( PARTITION BY CUSTOMER_ID, VALUE_CODE ORDER BY BEGIN_DATE DESC )
AS rn
FROM VALUES V
)
WHERE rn = 1
) V
ON ( C.CUSTOMER_ID = V.CUSTOMER_ID
AND VALUE_CODE = 'VOLUME' )
WHERE C.STATUS = 'ACTIVE'

Query showing wrong result

My below query gets records from table penilaian_header & penilaian_detail
select *
from
(
select
row, a.tanggal,b.OutletCode,c.Nilai,a.Nip,b.Fullname,a.KodePenilaian,
f.Description as posisilama, d.ShortDesc as posisibaru
from penilaian_header a
left join Employee b on a.Nip = b.Nip
left join Position f on b.PositionCode = f.PositionCode
left join Position d on a.PositionCode = d.PositionCode
left join Penilaian_Detail e on a.KodePenilaian = e.KodePenilaianH
left join arealeader g on g.OutletCode = b.OutletCode
left join (
select
ROW_NUMBER() OVER(PARTITION BY KodePenilaianH ORDER BY idPenilaiand DESC) AS Row,
Nilai,
KodePenilaianH
from penilaian_Detail
) c on a.KodePenilaian = e.KodePenilaianH
where a.Outlet like '%%' and Periode like '%%'
) nilai
pivot
(
sum(nilai) for row in ([1],[2],[3],[4],[5])
) piv;
My record in penilaian_header
My record in penilaian_detail
When I run my query, I get this result.
The result should be same as penilaian_detail. Please tell me how to fix it.
http://sqlfiddle.com/#!3/f7e4d/8
Thanks to #dhruvjoshi for explanation. Now the results are like what i'm expected
first, I delete this
left join Penilaian_Detail e on a.KodePenilaian = e.KodePenilaianH
then i change this
on a.KodePenilaian = e.KodePenilaianH to on a.KodePenilaian = c.KodePenilaianH
The problem was that because of JOINs you had repeated rows in result set for inner query.
See inner query fiddle evaluation
Corrected query is below
select *
from
(
select
distinct ---added distinct here to avoid repetition
row, a.tanggal,b.OutletCode,c.Nilai,a.Nip,b.Fullname,a.KodePenilaian,f.Description as posisilama,d.ShortDesc as posisibaru
from penilaian_header a
left join Employee b on a.Nip = b.Nip
left join Position f on b.PositionCode = f.PositionCode
left join Position d on a.PositionCode = d.PositionCode
left join Penilaian_Detail e on a.KodePenilaian = e.KodePenilaianH
left join arealeader g on g.OutletCode = b.OutletCode
left join (
select
ROW_NUMBER() OVER(PARTITION BY KodePenilaianH ORDER BY idPenilaiand DESC) AS Row,
Nilai,KodePenilaianH
from penilaian_Detail
) c on a.KodePenilaian = e.KodePenilaianH
) nilai
pivot
(
sum(nilai)
for row in ([1],[2],[3],[4],[5])
) piv;
SQL demo link

How do I find the oldest date in Group

I have a table where I need to get the oldest date from a group and be able to return all rows. I'm finding it difficult since I need to return the system_id field.
AssignedProfsHistory MatterID EffectiveDate
1 33434-3344 08/22/2005
2 33434-3344 07/12/2004
3 33434-3344 07/12/2004
4 21122-323 12/05/2007
5 43332-986 10/18/2014
6 43332-986 03/23/2013
So in this example, the rows for systemid 2 & 3 should return because they are tied for earliest date. The row for systemid 4 should return and systemid 6 should be returned.
This is what I have so far. Because I need to include the systemid(AssignedProfHistory) I'm not getting the results I need.
SELECT aph.AssignedProfsHistory,
m.MatterID,
Min(aph.EffectiveDate) as 'EffectiveDate'
from AssignedProfsHistory aph
INNER JOIN Matters m
ON aph.Matters = m.Matters
WHERE aph.AssignedType = 'Originating'
Group by m.matters,m.matterid,aph.assignedprofshistory
order by m.MatterID
Any idea how to get the results I need?
Thank you in advance.
select AssignedProfsHistory, MatterID, EffectiveDate
from (
SELECT
aph.AssignedProfsHistory,
m.MatterID,
aph.EffectiveDate,
row_number() over(partition by m.MatterID order by aph.EffectiveDate) as rn
from AssignedProfsHistory aph
INNER JOIN Matters m ON aph.Matters = m.Matters
WHERE aph.AssignedType = 'Originating'
) t
where rn = 1;
You can use the row_number window function to assign row numbers to dates for each matterid. Because the ordering is based on the ascending EffectiveDate, rows with the oldest date get assigned 1 and you select those.
If a matterid can have multiple rows with the oldest dates, you can use rank or dense_rank to get all the rows for the oldest date.
Since you want to keep the ties, I'd do it like this:
SELECT t2.AssignedProfsHistory, m.MatterID, t2.EffectiveDate
FROM (
SELECT MatterID, MIN(EffectiveDate) med
FROM AssignedProfsHistory
WHERE AssignedType = 'Originating'
GROUP BY MatterID
) t1
INNER JOIN AssignedProfsHistory t2 ON t2.MatterID = t1.MatterID
and t2.EffectiveDate = t1.med and t2.AssignedType = 'Originating'
INNER JOIN Matters m on m.Matters = t2.Matters
ORDER BY m.MatterId
Here is an SQLFiddle without the Matters table that demonstrates it can work, no windowing functions or CTE required, though a CTE would allow you to avoid repeating the AssignedType='Originating' condition.
this should get you what you need
with cte as (
SELECT aph.AssignedProfsHistory,
m.MatterID,
aph.EffectiveDate as 'EffectiveDate'
from AssignedProfsHistory aph
INNER JOIN Matters m
ON aph.Matters = m.Matters
WHERE aph.AssignedType = 'Originating'
Group by m.matters,m.matterid,aph.assignedprofshistory
)
select
AssignedProfsHistory,
MatterID,
EffectiveDate
from
cte
join (
select
min(EffectiveDate) min_effectivedate,
MatterID
from
cte
group by
MatterID
) b on cte.EffectiveDate = b.min_effectivedate and
cte.MatterID = b.MatterID
order by AssignedProfsHistory
First take the older date and then join it with your table.
WITH OlderAPH AS (
SELECT
AssignedProfsHistory,
Matters,
MIN(EffectiveDate) OlderDate
FROM AssignedProfsHistory
WHERE AssignedType = 'Originating'
GROUP BY Matters, AssignedProfsHistory )
SELECT
O.AssignedProfsHistory, M.MatterID, O.OlderDate
FROM OlderAPH O
INNER JOIN Matters M ON O.Matters = M.Matters
ORDER BY M.MatterID

Limiting result sets by future date - SQL

The Query below produces a record for each Entry in the SP_ScheduleEvent Table.
SELECT m.MaterialId, m.MaterialTitle, se.EventDateTime, c.ChannelName
FROM GB_Material m
LEFT OUTER JOIN SP_ScheduleEvent se on se.MaterialName = m.MaterialName
INNER JOIN SP_Schedule s on s.ScheduleID = se.ScheduleID
INNER JOIN GB_Channel c on c.ChannelID = s.ChannelID
WHERE LOWER(m.MaterialName) like '%foo%' OR LOWER(m.MaterialTitle) like '%foo%'
I want to limit the result set by the nearest future EventDateTime.
So per material name i would like to see one EventDateTime, which should be the nearest future date to the current time.
And lastly, a record may not exist in the SP_ScheduleEvent table for a particular materialname, in which case there should be null returned for the EventDateTime column
SQLFiddle
How would i go about doing this?
First, your LEFT JOIN is immaterial, because the subsequent joins make it an INNER JOIN. Either use LEFT JOIN throughout the FROM statement or switch to INNER JOIN.
I think you can use ROW_NUMBER():
SELECT t.*
FROM (SELECT m.MaterialId, m.MaterialName, m.MaterialTitle, se.EventDateTime,
ROW_NUMBER() over (PARTITION BY m.MaterialId OVER se.EventDateTime DESC) as seqnum
FROM GB_Material m INNER JOIN
SP_ScheduleEvent se
on se.MaterialName = m.MaterialName INNER JOIN
SP_Schedule s
on s.ScheduleID = se.ScheduleID INNER JOIN
GB_Channel c
on c.ChannelID = s.ChannelID
WHERE se.EventDateTime > getdate() AND
(LOWER(m.MaterialName) like '%foo%' OR LOWER(m.MaterialTitle) like '%foo%')
) t
WHERE seqnum = 1
ORDER BY se.EventDateTime;
Use the ROW_NUMBER() function:
WITH cte AS (
SELECT m.MaterialId, m.MaterialTitle, se.EventDateTime, c.ChannelName,
ROW_NUMBER() OVER (PARTITION BY m.MaterialId ORDER BY EventDateTime ASC) AS rn
FROM GB_Material m
LEFT OUTER JOIN SP_ScheduleEvent se on se.MaterialName = m.MaterialName
LEFT OUTER JOIN SP_Schedule s on s.ScheduleID = se.ScheduleID
LEFT OUTER JOIN GB_Channel c on c.ChannelID = s.ChannelID
WHERE LOWER(m.MaterialName) like '%foo%' OR LOWER(m.MaterialTitle) like '%foo%'
AND se.EventDateTime > GETDATE()
)
SELECT * FROM cte
WHERE rn=1

how to join a table on a subquery that uses order by and limit

For each row from table tClass matching a given where clause,
join on the first row in tEv, sorted by time, where tEv.class_id = tClass.class_id
The following code throws the error
ORA-01799: a column may not be outer-joined to a subquery
select
c.class_id,
c.class_name,
e.start_time,
e.ev_id
from
tClass c
left join tEv e on (
e.ev_id = (
select
ss1.ev_id
from (
select
ed.ev_id
from
tEvDisp ed,
tEv e
where
ed.class_id = c.class_id
and ed.viewable = 'Y'
and ed.display_until > localtimestamp
and e.ev_id = ed.ev_id
order by
e.start_time
) ss1
where
rownum = 1
)
)
where
c.is_matching = 'Y';
How can this be rewritten to do what is described?
The above is for oracle, but needs to work in sqlite (substituting where necessary)
No idea about SQLite - that would need to be a separate question if this doesn't work - but for Oracle you could do something like this:
select c.class_id,
c.class_name,
e.start_time,
e.ev_id
from tClass c
left join (
select class_id, ev_id, start_time
from (
select ed.class_id,
ed.ev_id,
e.start_time,
row_number() over (partition by ed.class_id order by e.start_time) as rn
from tEvDisp ed
join tEv e on e.ev_id = ed.ev_id
where ed.viewable = 'Y'
and ed.display_until > localtimestamp
)
where rn = 1
) e on e.class_id = c.class_id
where c.is_matching = 'Y';
This uses a subquery which finds the most tEv data, using an analytic row_number() to identify the latest data for each class_id, which is restricted by the rn = 1 filter.
That subquery, consisting of at most one row per class_id, is then used the left outer join against tClass.
This sort of construct should get you what you need. You can fix the details.
select c.classid
, c.classname
, temp.maxstarttime
from tClass c left join (
select c.classid id
max(e.start_time) maxstarttime
from tClass join tEv on tEv.classId = tClass.ClassId
where whatever
group by c.classid) temp on c.classid = temp.id