SQL Case on Where clause to different columns - sql

I've been trying to get a query that, based on a given condition (if isCurrent = 1 or not) should give me just one value/row based on the CurriculumId /which will be a parameter on a stored procedure).
This value should, in case isCurrent = 1 return to me the item with the most current StartDate but if isCurrent = 0 then it should give me the one with the most current EndDate.
The thing is that I only want one item per CurriculumId, ideally the one with isCurrent = 1 and the most current StartDate (ignoring the remaining rows) but, if there are no experiences with isCurrent = 1, then it should return to me the one with the most current EndDate.
My previous query was almost working but I still got the one with the most current StartDate (isCurrent = 1) AND the one with the most current EndDate when I want to retrieve just one or another.
I've come to the query bellow:
SELECT table.IntProfessionalExperienceId,
table.IsCurrent,
table.StartDate,
table.EndDate
FROM table
WHERE table.CurriculumId = 12
AND
CASE table.IsCurrent
WHEN 1
THEN
table.StartDate = (
SELECT max(table.StartDate)
FROM table
WHERE table.IsCurrent = 1
AND table.CurriculumId = 12
GROUP BY table.CurriculumId
)
ELSE
table.EndDate = (
SELECT max(table.EndDate)
FROM table
WHERE table.CurriculumId = 12
GROUP BY table.CurriculumId
)
END
Individually, the queries seem to be working OK and returning the supposed value although when ran as a whole I get the following errors:
Msg 102, Level 15, State 1, Line 8
Incorrect syntax near '='.
Msg 102, Level 15, State 1, Line 14
Incorrect syntax near ')'.
Msg 102, Level 15, State 1, Line 21
Incorrect syntax near ')'.
What in my syntax is wrong? I know from reading the errors what is wrong with the query but I just don't know how to fix it. And is it just the syntax or am I doing the query wrong to start with?

Split this into multiple conditions, like this:
SELECT
table.IntProfessionalExperienceId,
table.IsCurrent,
table.StartDate,
table.EndDate
FROM table
WHERE
table.CurriculumId = 12 AND
(
(
Table.IsCurrent = 1 AND
table.StartDate =
(
SELECT max(table.StartDate)
FROM table
WHERE
table.IsCurrent = 1 AND
table.CurriculumId = 12
GROUP BY table.CurriculumId
)
) OR
(
ISNULL(table.IsCurrent,0) != 1 AND
table.EndDate =
(
SELECT max(table.EndDate)
FROM table
WHERE table.CurriculumId = 12
GROUP BY table.CurriculumId
)
)
)
EDIT: another, arguably simpler approach would be to pre-aggregate the data you want in your WHERE clause so that you only need to call it a single time, rather than evaluate each row separately. Something like the following:
SELECT
table.IntProfessionalExperienceId,
table.IsCurrent,
table.StartDate,
table.EndDate
FROM
table
INNER JOIN
(
SELECT
MAX(table.EndDate) AS MaxEndDate,
MAX(CASE WHEN table.IsCurrent = 1 THEN table.StartDate END) AS MaxCurrentStartDate
FROM table
WHERE CurriculumID = 12
) MaxDates ON
(Table.IsCurrent = 1 AND Table.StartDate = MaxDates.MaxCurrentStartDate) OR
(ISNULL(Table.IsCurrent, 0) != 1 AND Table.EndDate = MaxDates.MaxEndDate)
WHERE
table.CurriculumId = 12

Give each row a rank in its curriculumid group, using ROW_NUMBER with an appropriate order by clause. Then only take the records ranked 1 (i.e. best matching).
select
intprofessionalexperienceid,
iscurrent,
startdate,
enddate
from
(
select mytable.*
row_number() over
(
partition by curriculumid
order by
case when iscurrent = 1 then 1 else 2 end,
case when iscurrent = 1 then startdate else enddate end desc
) as rn
from mytable
) ranked
where rn = 1;
(I know this doesn't actually answer your question, but is the straight-forward way to approach the problem in my opinion.)

Try to use CASE statemen this way:
SELECT table.IntProfessionalExperienceId,
table.IsCurrent,
table.StartDate,
table.EndDate
FROM table
WHERE table.CurriculumId = 12
AND table.EndDate = CASE
WHEN table.IsCurrent = 1
THEN (
SELECT max(table.StartDate)
FROM table
WHERE table.IsCurrent = 1
AND table.CurriculumId = 12
GROUP BY table.CurriculumId
)
ELSE
(
SELECT max(table.EndDate)
FROM table
WHERE table.CurriculumId = 12
GROUP BY table.CurriculumId
)
END

Related

I just started learning SQL and I couldn't do the query, can you help me?

There is a field in the sql query that I can't do. First of all, a new column must be added to the table below. The value of this column needs to be percent complete, so it's a percentage value. So for example, there are 7 values from Cupboard=1 shelves. Where IsCounted is here, 3 of them are counted. In other words, those with Cupboard = 1 should write the percentage value of 3/7 as the value in the new column to be created. If the IsCounted of the others is 0, it will write zero percent. How can I do this?
My Sql Code:
SELECT a.RegionName,
a.Cupboard,
a.Shelf,
(CASE WHEN ToplamSayım > 0 THEN 1 ELSE 0 END) AS IsCounted
FROM (SELECT p.RegionName,
r.Shelf,
r.Cupboard,
(SELECT COUNT(*)
FROM FAZIKI.dbo.PM_ProductCountingNew
WHERE RegionCupboardShelfTypeId = r.Id) AS ToplamSayım
FROM FAZIKI.dbo.DF_PMRegionType p
JOIN FAZIKI.dbo.DF_PMRegionCupboardShelfType r ON p.Id = r.RegionTypeId
WHERE p.WarehouseId = 45) a
ORDER BY a.RegionName;
The result is as in the picture below:
It looks like a windowed AVG should do the trick, although it's not entirely clear what the partitioning column should be.
The SELECT COUNT can be simplified to an EXISTS
SELECT a.RegionName,
a.Cupboard,
a.Shelf,
a.IsCounted,
AVG(a.IsCounted * 1.0) OVER (PARTITION BY a.RegionName, a.Cupboard) Percentage
FROM (
SELECT p.RegionName,
r.Shelf,
r.Cupboard,
CASE WHEN EXISTS (SELECT 1
FROM FAZIKI.dbo.PM_ProductCountingNew pcn
WHERE pcn.RegionCupboardShelfTypeId = r.Id
) THEN 1 ELSE 0 END AS IsCounted
FROM FAZIKI.dbo.DF_PMRegionType p
JOIN FAZIKI.dbo.DF_PMRegionCupboardShelfType r ON p.Id = r.RegionTypeId
WHERE p.WarehouseId = 45
) a
ORDER BY a.RegionName;

ORDER BY FIELD() not in

I have a set of records which I need to sort in particular order. Basically I need to sort like:
select * from Table
ORDER BY FIELD(code, 'int', (<> 'int' and <> 'nat'), 'nat')
Or
select * from Table
ORDER BY FIELD(id, 5, >= 10, 7)
How can I do it?
I think this conditional sorting meets your requirement:
select * from Table
order by case
when id = 5 then 1
when id >= 10 then 2
when id = 7 then 3
else 4
end
or for the 1st case:
select * from Table
order by case
when code = 'int' then 1
when code <> 'nat' then 2
else 3
end
From your second select statement, You can try -
select * from Table
ORDER BY CASE WHEN id = 5 THEN 1
WHEN id BETWEEN 6 AND 10 AND id NOT <> 7 THEN 2
WHEN id = 7 THEN 3 END)
You can also use multiple keys in order by:
order by (id = 5) desc, -- "5" first
(id = 7) asc -- "7" last
I am using MySQL syntax because FIELD() is a MySQL function.

Comparing two max dates with a condition in Oracle SQL

I have the data as below
ID date state
1 24-Aug-18 Not defined
1 23-Aug-18 Incorrect
1 22-Aug-18 Incorrect
1 21-Aug-18 Incorrect
1 1-Aug-18 Correct
1 23-Jul-17 Incorrect
1 22-Jul-17 Incorrect
1 21-Jul-17 Incorrect
1 10-Jul-17 Correct
The record 1 can stay at the incorrect state for 3 days post that it goes to 'not defined' (unless any update has not been made to the record. If done then it gets back to Correct). The not defined state has to be avoided. Now I need to define a query such that the query can identify the minimum latest record date at which the record went to the incorrect state i.e. in this case 21-Aug-2018. Also problem here is the table doesn't have unique keys.
I have been trying the below code but it is throwing me the error
'ORA-01427: single-row subquery returns more than one row'
select id, min(date) from table where state = 'Incorrect' group by id having
((Select trunc(MAX (date)) from table where state = 'Incorrect'
group by id) >= (select trunc(Max (date)) from table where state = 'Correct'
group by id))
Hmmm, I think this does what you want:
select id, min(date) as min_latest_incorrect_date
from (select t.*,
max(case when state = 'Correct' then date end) over (partition by id) as max_date_correct
from t
) t
where (date > max_date_correct or max_date_correct is null) and
state = 'Incorrect'
group by id
Per ID, you are looking for incorrect records that are not followed by any correct record. Of these take the first.
select id, min(date)
from mytable i
where state = 'Incorrect'
and not exists
(
select *
from mytable c
where c.id = i.id
and c.state = 'Correct'
and c.date > i.date
)
group by id
order by id;

SQL Group By and assign highest a value

I am struggling with the SQL (mssql) to manipulate my data as i need it. I have a table like this;
SOMEID, SOMEFIELD, DATE
5 True 01-01-2010
5 True 01-01-2011
5 False 05-05-2012
7 True 05-05-2011
7 False 06-07-2015
What I am trying to achieve is to add another column which assigns the value 1 if they are the most recent for that ID, and 0 if not. So in the above data example the new column values from top to bottom would be 0, 0, 1, 0, 1.
I know I need to group by date but am having trouble assigning the values.
Thanks for any pointers!
You can use row_number() in SQL Server like this:
select *
, case when (row_number() over (partition by SOMEID order by [Date] desc)) = 1 then 1 else 0 end seq
from
yourTable
order by
SOMEID, [Date];
SQL Fiddle Demo
You can use a self join to get the highest row per group then in update query use a case statement to assign value to new column
update a
set a.[somecol] = case when b.[SOMEID] is null then 1 else 0 end
from demo a
left join demo b on a.[SOMEID] = b.[SOMEID]
and a.[DATE] < b.[DATE]
DEMO
try this
SELECT SOMEID, SOMEFIELD, DATE
, CASE WHEN (SELECT MAX(SubTab.Date)
FROM myTable SubTab
WHERE SubTab.SOMEID = myTable.SOMEID
) = myTable.DATE
THEN 1 ELSE 0 END
FROM myTable

How to get data based on Case condition and MAX Date

I have some data:
Declare #table table (RID VARCHAR(10),
CommType INT,
CommunicationType INT,
VALUE VARCHAR(20),
lastDate Datetime)
INSERT INTO #table (RID, CommType, CommunicationType, VALUE, lastDate)
VALUES
('00WAAS', 3, 0, 'mohan#gmail', '2012-06-15 15:23:49.653'),
('00WAAS', 3, 1, 'manasa#gmail', '2015-08-15 15:23:49.653'),
('00WAAS', 3, 2, 'mother#gmail', '2014-09-15 15:23:49.653'),
('00WAAS', 3, 2, 'father#gmail', '2016-01-15 15:23:49.653'),
('00WAAS', 3, 0, 'hello#gmail', '2013-01-15 15:23:49.653')
My query:
SELECT
TT.RID,
COALESCE(Homemail, BusinessMail, OtherMail) Mail
FROM
(SELECT
RID, MAX(Homemail) Homemail,
MAX(BusinessMail) BusinessMail,
MAX(OtherMail) OtherMail
FROM
(SELECT
RID,
CASE
WHEN CommType = 3 AND CommunicationType = 0 THEN VALUE
END AS Homemail,
CASE
WHEN CommType = 3 AND CommunicationType = 1 THEN VALUE
END AS BusinessMail,
CASE
WHEN CommType = 3 AND CommunicationType = 2 THEN VALUE
END AS OtherMail,
lastDate
FROM
#table) T
GROUP BY RID) TT
What I'm expecting
Here I need to get result if CommType = 3 and CommunicationType = 0 then related value based on latest date and if data is not available for
CommType = 3 and CommunicationType = 0
then I need to get data of CommunicationType = 1
related value based on latest date and if there is no data for
CommunicationType = 1
then CommunicationType = 2 based on latest date of that CommunicationTypes.
Here I have tried Case condition ,MAX and Coalesce
If combination data is present in CommunicationType = 0 is present get CommunicationType = 0 based on latest date
If combination data is not present in CommunicationType = 0 then get CommunicationType = 1 based on latest date
If combination data is not present in CommunicationType = 1 then get CommunicationType = 2 based on latest date
I'm not entirely sure I've understood the requirement. But I think you want:
One record returned for each RID.
The returned record should have a CommType of 3.
If there is more than one record with a CommType 3 you want the record with the lowest CommunicationType.
If there is still more than one record you want the one with the most recent lastDate.
This query uses the windowed function ROW_NUMBER to rank the available records, within a subquery. PARTITION BY ensures each RID is ranked sepearatly. The outer query returns all records with a rank of 1.
Query
SELECT
r.*
FROM
(
/* For each RID We want the lowest communication type with
* the most recent last date.
*/
SELECT
ROW_NUMBER() OVER (PARTITION BY RID ORDER BY CommunicationType, lastDate DESC) AS rn,
*
FROM
#table
WHERE
CommType = 3
) AS r
WHERE
r.rn = 1
;
Next Steps
This query is ok but could be better. For example what would happen if two records had a matching CommType, CommunicationType and lastDate? Reading up on the differences between ROW_NUMBER, RANK, DENSE_RANK and NTILE will help you figure out your options here.
If I understood you correctly, use ROW_NUMBER() :
SELECT tt.RID,COALESCE(tt.Homemail,tt.businessMail,tt.OtherMail)
FROM(
select s.RID,
MAX(CASE WHEN s.CommType = 3 AND s.CommunicationType = 0 THEN s.VALUE END) AS Homemail,
MAX(CASE WHEN s.CommType = 3 AND s.CommunicationType = 1 THEN s.VALUE END) AS BusinessMail,
MAX(CASE WHEN s.CommType = 3 AND s.CommunicationType = 2 THEN s.VALUE END) AS OtherMail
from (SELECT t.*,ROW_NUMBER() OVER(PARTITION BY t.rid,t.communicationType ORDER BY t.lastDate DESC)
FROM #table t
WHERE t.commType = 3) s
WHERE s.rnk = 1
GROUP BY s.rid) tt