SQL Group By and assign highest a value - sql

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

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;

Check whether an employee is present on three consecutive days

I have a table called tbl_A with the following schema:
After insert, I have the following data in tbl_A:
Now the question is how to write a query for the following scenario:
Put (1) in front of any employee who was present three days consecutively
Put (0) in front of employee who was not present three days consecutively
The output screen shoot:
I think we should use case statement, but I am not able to check three consecutive days from date. I hope I am helped in this
Thank you
select name, case when max(cons_days) >= 3 then 1 else 0 end as presence
from (
select name, count(*) as cons_days
from tbl_A, (values (0),(1),(2)) as a(dd)
group by name, adate + dd
)x
group by name
With a self-join on name and available = 'Y', we create an inner table with different combinations of dates for a given name and take a count of those entries in which the dates of the two instances of the table are less than 2 units apart i.e. for each value of a date adate, it will check for entries with its own value adate as well as adate + 1 and adate + 2. If all 3 entries are present, the count will be 3 and you will have a flag with value 1 for such names(this is done in the outer query). Try the below query:
SELECT Z.NAME,
CASE WHEN Z.CONSEQ_AVAIL >= 3 THEN 1 ELSE 0 END AS YOUR_FLAG
FROM
(
SELECT A.NAME,
SUM(CASE WHEN B.ADATE >= A.ADATE AND B.ADATE <= A.ADATE + 2 THEN 1 ELSE 0 END) AS CONSEQ_AVAIL
FROM
TABL_A A INNER JOIN TABL_A B
ON A.NAME = B.NAME AND A.AVAILABLE = 'Y' AND B.AVAILABLE = 'Y'
GROUP BY A.NAME
) Z;
Due to the complexity of the problem, I have not been able to test it out. If something is really wrong, please let me know and I will be happy to take down my answer.
--Below is My Approch
select Name,
Case WHen Max_Count>=3 Then 1 else 0 end as Presence
from
(
Select Name,MAx(Coun) as Max_Count
from
(
select Name, (count(*) over (partition by Name,Ref_Date)) as Coun from
(
select Name,adate + row_number() over (partition by Name order by Adate desc) as Ref_Date
from temp
where available='Y'
)
) group by Name
);
select name as employee , case when sum(diff) > =3 then 1 else 0 end as presence
from
(select id, name, Available,Adate, lead(Adate,1) over(order by name) as lead,
case when datediff(day, Adate,lead(Adate,1) over(order by name)) = 1 then 1 else 0 end as diff
from table_A
where Available = 'Y') A
group by name;

SQL check if the value is more than 4 then update the other column stop the updates

I I have one tables updating two columns if the second column the update more or equal to 4 set the other column to 0
Tried case statement .... but still not working please help!
update [dbo].[QueuedSms_TEST]
set issent = 0,pendingstatusid = pendingstatusid + 1
where sendresponse is null
while (select pendingstatusid from [dbo].[QueuedSms_TEST]) > 3
update [dbo].[QueuedSms_TEST]
set issent = -1
First you create a temporal query to get a row_number, and then update that with the required rules.
SQL Demo
WITH cte as (
SELECT *,
ROW_NUMBER() OVER (ORDER BY pendingstatusid) as rn
FROM QueuedSms_TEST
)
UPDATE cte
SET pendingstatusid = rn,
issent = CASE WHEN rn > 3
THEN -1
ELSE 0
END;

Null value in order by clause

I have a weird scenario, in which I need to keep all the rows at top in which X column has NULL value else sort by Y column. Can you help me in writing query.
ORDER BY CASE WHEN X IS NULL THEN 0 ELSE 1 END, Y
You can use a CASE statement in ORDER BY:
ORDER BY
CASE WHEN X IS NULL THEN 0 ELSE 1 END ASC, Y
Here you go, this will work with any sql platform -- for a specific platform there might be a better way to do it.
SELECT * FROM
(
SELECT 1 AS orderC, *
FROM tableName
WHERE Xcolumn is null
UNION ALL
SELECT 2 AS orderC, *
FROM tableName
WHERE Xcolumn is not null
)
ORDER BY orderC ASC, columnY
Note, if you don't want orderC to be in the output, just specify all the other columns in the outer select.
Sharing what I learned before using:
ORDER BY FIELD(Xcolumn, NULL) DESC, Ycolumn DESC
SELECT * FROM TABLENAME ORDER BY X ,Y
You can use query like below:
SELECT * FROM Emp WHERE empId= 6 AND DELETED = 0
ORDER BY CASE WHEN DOB IS NULL THEN 0 ELSE 1 END
, CREATETIMESTAMP.
for more details you can see here

How do I use the value from row above when a given column value is zero?

I have a table of items by date (each row is a new date). I am drawing out a value from another column D. I need it to replace 0s though. I need the following logic: when D=0 for that date, use the value in column D from the date prior.
Actually, truth be told, I need it to say, when D is 0, use the value from the latest date where D was not a 0, but the first will get me most of the way there.
Is there a way to build this logic? Maybe a CTE?
Thank you very much.
PS I'm using SSMS 2008.
EDIT: I wasn't very clear at first. The value I want to change is not the date. I want change the value in D with the latest non-zero value from D, based on date.
May be the following query might help you. It uses the OUTER APPLY to fetch the results. Screenshot #1 shows the sample data and query output against the sample data. This query can be written better but this is what I could come up with right now.
Hope that helps.
SELECT ITM.Id
, COALESCE(DAT.New_D, ITM.D) AS D
, ITM.DateValue
FROM dbo.Items ITM
OUTER APPLY (
SELECT
TOP 1 D AS New_D
FROM dbo.Items DAT
WHERE DAT.DateValue < ITM.DateValue
AND DAT.D <> 0
AND ITM.D = 0
ORDER BY DAT.DateValue DESC
) DAT
Screenshot #1:
UPDATE t
Set value = SELECT value
FROM table
WHERE date = (SELECT MAX(t1.date)
FROM table t1
WHERE t1.value != 0
AND t1.date < t.date)
FROM table t
WHERE t.value = 0
You could maybe something like this as part of an update script...
SET myTable.D = (
SELECT TOP 1 myTable2.D
FROM myTable2
WHERE myTable2.myDateField < myTable.myDateField
AND myTable2.D != 0
ORDER BY myTable2.myDateField DESC)
That's assuming that you want to actually update the data though rather than just replace the values for the purpose of a select query.
How about:
SELECT
i.ID,
i.DateValue,
D = CASE WHEN I.D <> 0 THEN I.D ELSE X.D END
FROM
Items I
OUTER APPLY (
SELECT TOP 1 S.D
FROM Items S
WHERE S.DATEVALUE < I.DATEVALUE AND S.D <> 0
ORDER BY S.DATEVALUE DESC
) X
SELECT t.id,
CASE WHEN t.D = 0 THEN t0.D
ELSE t.D END
FROM table AS t
LEFT JOIN table AS t0
ON t0.time =
(
SELECT MAX(time) FROM t0
WHERE t0.time < t.time
AND t0.D != 0
)
or if you want to avoid aggregates entirely,
SELECT t.id,
CASE WHEN t.D = 0 THEN t0.D
ELSE t.D END
FROM table AS t
LEFT JOIN table AS t0
ON t0.time < t.time
LEFT JOIN table AS tx
ON tx.time > t0.time
WHERE t0.D != 0
AND tx.D != 0
AND tx.id IS NULL -- i.e. there isn't any