Get Max in a Group based on a condition - sql-server-2008

ProjID Dno RNo Status DateApproved
100 1 1 Initiated 2014-12-31 09:15:58.000
100 1 1 Approved 2015-01-31 09:15:58.000
100 1 1 Approved 2015-02-01 09:15:58.000
100 1 1 Approved 2015-05-28 09:15:58.000
100 1 1 Approved 2015-06-20 09:15:58.000
101 1 1 Approved 2014-12-31 09:15:58.000
101 1 1 Approved 2015-01-31 09:15:58.000
101 1 1 Approved 2015-02-01 09:15:58.000
101 1 1 Approved 2015-05-28 09:15:58.000
101 1 1 Approved 2015-08-20 09:15:58.000
In the above example i have to get max(Dateapproved) as Dateapproved for each projectid.
if all the revision Status are approved in a particular group for eg :project id=101 has all rows in its group having a status as Approved so i have to get the max date : '2015-08-20 09:15:58.000'.But for Projectid=100 one status is still in Initiated State so we have to show Null as Dateapproved .
Thanks in Advance
My output should be like:
ProjId Dno Rno DateApproved
100 1 1 NUll
101 1 1 2015-08-20 09:15:58.000
Example code:
Create table #temp(
ProjectID varchar(35),
Documentno int,
Revisionno int,
Status varchar(35),
DateApproved Datetime)
insert into #temp values ( '100', 1, 1, 'Initiated','2014-12-31 09:15:58')
insert into #temp values ( '100', 1, 1, 'Approved','2015-01-31 09:15:58 ')
insert into #temp values ( '100', 1, 1, 'Approved','2015-02-01 09:15:58 ')
insert into #temp values ( '100', 1, 1, 'Approved','2015-05-28 09:15:58 ')
insert into #temp values ( '100', 1, 1, 'Approved','2015-06-20 09:15:58 ')
insert into #temp values ( '101', 1, 1, 'Approved','2014-12-31 09:15:58 ')
insert into #temp values ( '101', 1, 1, 'Approved','2015-01-31 09:15:58 ')
insert into #temp values ( '101', 1, 1, 'Approved','2015-02-01 09:15:58 ')
insert into #temp values ( '101', 1, 1, 'Approved','2015-05-28 09:15:58 ')
insert into #temp values ( '101', 1, 1, 'Approved','2015-08-20 09:15:58 ')
select * from #temp

Try this:
SELECT T.ProjectID,
Documentno as Dno,
Revisionno as RNo,
CASE WHEN SUM(CASE WHEN T.Status <> 'Approved' THEN 1 ELSE 0 END) = 0
THEN Max(T.DateApproved) ELSE NULL
END as DateApproved
from #temp T
GROUP BY T.ProjectId, Documentno , Revisionno
This gives the following output when run against your test data:
PROJECT ID DNo TNo DateApproved
100 1 1 NULL
101 1 1 2015-08-20 09:15:58.000

You can do this with a case statement and a conditional aggregate. Get the count of statuses that are not Approved using COUNT(NULLIF(Status, 'Approved')). If this is 0 then get the max date approved:
SELECT ProjectID,
DateApproved = CASE WHEN COUNT(NULLIF(Status, 'Approved')) = 0 THEN MAX(DateApproved) END
FROM #Temp
GROUP BY ProjectID;

Related

Calculate the period of validity of the price

I have a table with an item, its cost and the date it was added.
CREATE TABLE item_prices (
item_id INT,
item_name VARCHAR(30),
item_price DECIMAL(12, 2),
created_dttm DATETIME
);
INSERT INTO item_prices(item_id, item_name, item_price, created_dttm) VALUES
(1, 'spoon', 10.20 , '2023-01-01 01:00:00'),
(1, 'spoon', 10.20 , '2023-01-08 01:35:00'),
(1, 'spoon', 10.35 , '2023-01-14 15:00:00'),
(2, 'table', 40.00 , '2023-01-01 01:00:00'),
(2, 'table', 40.00 , '2023-01-03 11:22:00'),
(2, 'table', 41.00 , '2023-01-10 08:28:22'),
(1, 'spoon', 10.35 , '2023-01-28 21:52:00'),
(1, 'spoon', 11.00 , '2023-02-15 16:36:00'),
(2, 'table', 41.00 , '2023-02-16 21:42:11'),
(2, 'table', 45.20 , '2023-02-19 20:25:25'),
(1, 'spoon', 9.00 , '2023-03-02 14:50:00'),
(1, 'spoon', 9.00 , '2023-03-06 16:36:00'),
(1, 'spoon', 8.50 , '2023-03-15 12:00:00'),
(2, 'table', 30 , '2023-03-05 10:10:10'),
(2, 'table', 30 , '2023-03-10 15:45:00');
I need to create a new table with the following fields:
"item_id",
"item_name",
"item_price",
"valid_from_dt": date on which the price was effective (created_dttm price record)
"valid_to_dt": date until which this price was valid (created_dttm of the next record for this product "minus" one day)
I thought it might be possible to start by selecting days on which new entries are added with new prices with such a request:
SELECT item_id, item_name, item_price,
MIN(created_dttm) as dt
FROM table
GROUP BY item_price, item_id, item_name
that provides me this output:
The expected output is the following:
item_id
item_name
item_price
valid_from_dt
valid_to_dt
1
spoon
10.20
2023-01-01
2023-01-13
1
spoon
10.35
2023-01-14
2023-02-14
1
spoon
11.00
2023-02-15
2023-03-01
1
spoon
9.00
2023-03-02
2023-03-01
1
spoon
8.50
2023-03-15
2023-03-14
2
table
40.00
2023-01-01
2022-01-09
2
table
41.00
2023-01-10
2023-02-18
....
....
....
....
....
select distinct
item_id,
item_name,
first_value(item_price) over (partition by item_id order by created_dttm) as item_price,
min(created_dttm) over (partition by item_id ) as valid_from_dt,
max(created_dttm) over (partition by item_id ) as valid_to_dt
from item_prices
;
output:
item_id
item_name
item_price
valid_from_dt
valid_to_dt
1
spoon
10.20
2023-01-01 01:00:00
2023-03-15 12:00:00
2
table
40.00
2023-01-01 01:00:00
2023-03-10 15:45:00
see: DBFIDDLE
Your query is correct. It's only missing the next step:
retrieving the next "valid_from_dt" in the partition <item_id, item_name>, using the LEAD function
subtract 1 day from it
WITH cte AS (
SELECT item_id, item_name, item_price,
MIN(created_dttm) AS valid_from_dt
FROM item_prices
GROUP BY item_id, item_name, item_price
)
SELECT *,
LEAD(valid_from_dt) OVER(PARTITION BY item_id, item_name) - INTERVAL 1 DAY AS valid_to_dt
FROM cte
Check the demo here.

MYSQL set column based first record for each group

I have a table which can be recreated as
create table test
(employeeID int,
date Date,
TookTest int
);
insert into test(employeeID, date, TookTest)
values
(1, '2014-01-01', 1),
(1, '2014-01-02', 1),
(1, '2014-01-03', 1),
(2, '2014-01-01', 1),
(2, '2014-01-20', 1),
(3, '2014-01-01', 1),
(3, '2014-01-21', 1),
(4, '2014-01-03', 1),
(4, '2014-01-27', 1)
I am trying to set the first record of every group to be 0 in the newCol
employeeID date TookTest newCol
----------------------------------------
1 2014-01-01 1 0
1 2014-01-02 1 1
1 2014-01-03 1 1
2 2014-01-01 1 0
2 2014-01-20 1 1
3 2014-01-01 1 0
3 2014-01-21 1 1
4 2014-01-03 1. 0
4 2014-01-27 1. 1
How do I go about this?
UPDATE test t1
JOIN ( SELECT t2.employeeID, MIN(t2.`date`) `date`
FROM test t2
GROUP BY 1 ) t3 USING (employeeID, `date`)
SET t1.TookTest = 0;

How will I fix this table using MySQL?

I am new to MySQL. I am using MySQL 8.0.
My schema and sample data is as follows:
CREATE TABLE AA (
A int
);
insert into AA (A) values (1);
insert into AA (A) values (1);
insert into AA (A) values (2);
insert into AA (A) values (2);
insert into AA (A) values (1);
insert into AA (A) values (3);
CREATE TABLE BB (
code int,
description varchar(30)
);
insert into BB (code, description) values (1, 'Male');
insert into BB (code, description) values (2, 'Female');
Here's my code
with totalcount as (
select code as 'CODE',
description as 'SEX',
count(A) AS 'TOTAL',
ROUND((COUNT(A) * 100.0) / (SELECT COUNT(A) FROM AA),2) AS 'PERCENT',
sum(count(A)) over (order by CODE asc) AS 'CUMULATIVE',
ROUND((SUM(COUNT(A)) OVER (ORDER BY CODE ) / (SELECT COUNT(A) FROM AA) *100 ),2) AS 'CUMPERCENT'
from AA, BB
where AA.A= BB.code
group by AA.A
)
select *
from totalcount
UNION ALL
SELECT '0' CODE, 'TOTAL' SEX, SUM(TOTAL), SUM(PERCENT), '0' CUMULATIVE, '0.00' CUMPERCENT
from totalcount
ORDER BY CODE;
The output was this
CODE | SEX | TOTAL | PERCENT | CUMULATIVE | CUMPERCENT
0 TOTAL 5 100.00 0 0.00
1 MALE 3 60.00 3 60.00
2 FEMALE 2 40.00 5 100.00
I used UNION since I want the total of these items. In this case, I inserted 0 in TOTAL of SEX column to put it in the first row. Is there another way aside from using UNION and inserting code 0?
As you can see from my table BB, there are only code 1 and 2 for male and female, respectively. I want to show that the value not in this table should be in NOT VALID.
CODE | SEX | TOTAL | PERCENT | CUMULATIVE | CUMPERCENT
0 TOTAL 6 100.00 0 0.00
1 MALE 3 50.00 3 50.00
2 FEMALE 2 33.33 5 83.33
3 NOT VALID 1 16.67 6 100.00
Can you help me with this? Thank you.
This is a very tricky reporting question. One approach uses GROUP BY ROLLUP to generate the total row, along with judicious use of COALESCE to fill in the missing values per your requirements.
SELECT
COALESCE(t1.A, 0) AS CODE,
CASE WHEN t1.A IS NOT NULL THEN COALESCE(t2.DESCRIPTION, 'NOT VALID') ELSE 'TOTAL' END AS SEX,
t1.TOTAL,
ROUND(100.0 * t1.TOTAL / SUM(CASE WHEN t1.A IS NOT NULL THEN t1.TOTAL ELSE 0 END) OVER (), 2) AS PERCENT,
SUM(CASE WHEN t1.A IS NOT NULL THEN t1.TOTAL ELSE 0 END) OVER (ORDER BY t1.A) AS CUMULATIVE,
ROUND(100.0 * SUM(CASE WHEN t1.A IS NOT NULL THEN t1.TOTAL ELSE 0 END) OVER (ORDER BY t1.A) /
SUM(CASE WHEN t1.A IS NOT NULL THEN t1.TOTAL ELSE 0 END) OVER (), 2) AS CUMPERCENT
FROM
(
SELECT
A,
COUNT(*) AS TOTAL
FROM AA
GROUP BY A WITH ROLLUP
) t1
LEFT JOIN BB t2
ON t2.CODE = t1.A
ORDER BY
CODE;
Demo

SQL divide many records by day

my SQL table's struct is very simple,only contains 3 fields:
createDate(Date): time when record inserted;
title(String): title for record;
count(Integer32): count for record;
There 10w+ records in the table! Represents records inserted in one year:
Any day could inserted any number records(include 0 record)
So,How could I divide records by days???
eg: There 10 records in the table:
1. 2019-01-01 10:20:15 xxx
2. 2019-01-01 12:50:10 xxx
3. 2019-01-01 23:20:19 xxx
4. 2019-01-02 10:20:15 xxx
5. 2019-01-05 08:20:15 xxx
6. 2019-01-05 22:20:15 xxx
7. 2019-02-10 10:20:15 xxx
8. 2019-02-10 11:20:15 xxx
9. 2019-02-10 15:20:15 xxx
10. 2019-02-15 10:20:15 xxx
I want result : divide to 5 "collections"
collection "2019-01-01" (contain 3 records):
- 2019-01-01 10:20:15 xxx
- 2019-01-01 12:50:10 xxx
- 2019-01-01 23:20:19 xxx
collection "2019-01-02" (contain 1 record):
- 2019-01-02 10:20:15 xxx
collection "2019-01-05" (contain 2 records):
- 2019-01-05 08:20:15 xxx
- 2019-01-05 22:20:15 xxx
collection "2019-02-10" (contain 3 records):
- 2019-02-10 10:20:15 xxx
- 2019-02-10 11:20:15 xxx
- 2019-02-10 15:20:15 xxx
collection "2019-02-15" (contain 1 record):
- 2019-02-15 10:20:15 xxx
If my table schema is correct then this would be your possible solution.
GO
CREATE TABLE #tempRequestForMeList
(
createDate datetime,
title nvarchar(50),
[count] int
)
GO
insert into #tempRequestForMeList ( createDate, title, [count] )
values ( '2016-09-20 17:17:04.840', 'dd', 0 )
, ( '2016-09-20 17:17:04.840', 'dd', 1 )
, ( '2016-09-20 07:17:04.840', 'dd', 1 )
, ( '2016-09-20 05:17:04.840', 'dd', 1 )
, ( '2016-09-20 13:17:04.840', 'dd', 1 )
, ( '2016-09-19 12:17:04.840', 'dd', 1 )
, ( '2016-09-19 02:17:04.840', 'dd', 1 )
, ( '2016-09-19 01:17:04.840', 'dd', 1 )
, ( '2016-09-18 02:17:04.840', 'dd', 1 )
, ( '2016-09-18 03:17:04.840', 'dd', 1 )
, ( '2016-09-18 05:17:04.840', 'dd', 1 )
, ( '2016-09-18 07:17:04.840', 'dd', 1 )
GO
; with cte as (
select cast(createdate as date) as Date1, * from #tempRequestForMeList )
update dd set dd.[count] = ct.co from #tempRequestForMeList as dd inner join (select count(date1) as co, date1 from cte group by Date1) as ct on cast(dd.createDate as DATE) = ct.Date1
select * from #tempRequestForMeList --- if require count with each row
go
drop table #tempRequestForMeList
go
If this doesn't work then show your table schema and expected output.
Note: This is for SQL server
Try to use COUNT by PARTITION:
SELECT
t.*
, count( CONVERT(date, t.createDate)) OVER (PARTITION BY CONVERT(date, t.createDate)
ORDER BY CONVERT(date, t.createDate)) CountByDate
FROM
#tempRequestForMeList t
Let me show an example(Thanks to #DarkRob for sample data):
DECLARE #tempRequestForMeList TABLE
(
createDate DATETIME,
title NVARCHAR(50),
[count] INT
);
INSERT INTO #tempRequestForMeList
(
createDate,
title,
count
)
VALUES
('2016-09-20 17:17:04.840', 'dd', 0),
('2016-09-20 17:17:04.840', 'dd', 1),
('2016-09-20 07:17:04.840', 'dd', 1),
('2016-09-20 05:17:04.840', 'dd', 1),
('2016-09-20 13:17:04.840', 'dd', 1),
('2016-09-19 12:17:04.840', 'dd', 1),
('2016-09-19 02:17:04.840', 'dd', 1),
('2016-09-19 01:17:04.840', 'dd', 1),
('2016-09-18 02:17:04.840', 'dd', 1),
('2016-09-18 03:17:04.840', 'dd', 1),
('2016-09-18 05:17:04.840', 'dd', 1),
('2016-09-18 07:17:04.840', 'dd', 1),
('2016-10-20 17:17:04.840', 'dd', 0);
and query:
SELECT
t.*
, count( CONVERT(date, t.createDate)) OVER (PARTITION BY CONVERT(date, t.createDate)
ORDER BY CONVERT(date, t.createDate)) CountByDate
FROM
#tempRequestForMeList t
OUTPUT:
createDate title count CountByDate
2016-09-18 02:17:04.840 dd 1 4
2016-09-18 03:17:04.840 dd 1 4
2016-09-18 05:17:04.840 dd 1 4
2016-09-18 07:17:04.840 dd 1 4
2016-09-19 12:17:04.840 dd 1 3
2016-09-19 02:17:04.840 dd 1 3
2016-09-19 01:17:04.840 dd 1 3
2016-09-20 17:17:04.840 dd 0 5
2016-09-20 17:17:04.840 dd 1 5
2016-09-20 07:17:04.840 dd 1 5
2016-09-20 05:17:04.840 dd 1 5
2016-09-20 13:17:04.840 dd 1 5
2016-10-20 17:17:04.840 dd 0 1

Group by with text and date time combination

I have a table
id col1 col2 namecol1 datetime1 teamcol1 namecol2 datetime2
1 12345 2345 name1 2014-10-13 11:57:24.713 teama
2 12345 2345 name1 2014-10-13 11:57:24.713 teamb abc 2014-11-29 09:55:38.533
3 12345 2345 name1 2014-10-13 11:57:24.713 teamb bcd 2014-12-02 06:35:38.917
4 12345 2345 name1 2014-10-13 11:57:24.713 teamc def 2014-12-22 11:57:54.863
5 12345 2345 name1 2014-10-13 11:57:24.713 teamd efg 2015-01-03 13:28:24.717
I need this output:
col1 col2 Team1 DateTime1 Team2 DateTime2 Team3 DateTime3
12345 2345 bcd 2014-12-02 06:35:38.917 def 2014-12-22 11:57:54.863 efg 2015-01-03 13:28:24.717
I tried this query:
SELECT
MAX(CASE WHEN teamcol1='teamb' THEN namecol2 END) AS Team1,
CONVERT(DATE, MAX(CASE WHEN teamcol1='teamb' THEN datetime2 END), 105) AS DateTime1,
MAX(CASE WHEN teamcol1='teamc' THEN namecol2 END) AS PRECON_AUDIT,
CONVERT(DATE,MAX(CASE WHEN teamcol1='teamc' THEN datetime2 END), 105) AS DateTime2,
MAX(CASE WHEN teamcol1 IN ('teamd') THEN namecol2 END) AS Team3,
CONVERT(DATE,MAX(CASE WHEN teamcol1 IN ('teamd') THEN datetime2 END),105) AS DateTime3,
col1, col2
FROM
(SELECT *
FROM table1) Z
WHERE
col1 = '12345'
GROUP BY
col1, col2
Output of this query:
col1 col2 Team1 DateTime1 Team2 DateTime2 Team3 DateTime3
12345 2345 abc 2014-12-02 06:35:38.917 def 2014-12-22 11:57:54.863 efg 2015-01-03 13:28:24.717
I am using SQL Server 2008.
Thanks in advance.
[EDIT]
Table1 is like audit table which will have multiple entries with different combination of col1,col2.
I need to display for each combination of col1,col2 different team names and with appropriate namecol2 and datetime2 columns.
When i use the query mentioned above it is giving me the output correctly if there is no repetition in teamcol1. If there is a repetition in teamcol1 (as mentioned in question) it is giving me the wrong namecol2.
In case of repetition in teamcol1 i need latest namecol2 and datetime2 ( from the table i need namecol2 -- bcd and datetime2 -- 2014-12-02 06:35:38.917)
At first make a list of all groups (DISTINCT col1, col2).
Then for each group find one row with the latest datetime2. Do it three times, for each teamb, teamc, teamd.
You can put complex query inside OUTER APPLY with whatever logic and ordering you need.
DECLARE #T TABLE (id int, col1 int, col2 int, namecol1 varchar(255), [datetime1] datetime, teamcol1 varchar(255), namecol2 varchar(255), [datetime2] datetime);
INSERT INTO #T (id, col1, col2, namecol1, datetime1, teamcol1, namecol2, datetime2) VALUES (1, 12345, 2345, 'name1', '2014-10-13 11:57:24.713', 'teama', NULL, NULL);
INSERT INTO #T (id, col1, col2, namecol1, datetime1, teamcol1, namecol2, datetime2) VALUES (2, 12345, 2345, 'name1', '2014-10-13 11:57:24.713', 'teamb', 'abc', '2014-11-29 09:55:38.533');
INSERT INTO #T (id, col1, col2, namecol1, datetime1, teamcol1, namecol2, datetime2) VALUES (3, 12345, 2345, 'name1', '2014-10-13 11:57:24.713', 'teamb', 'bcd', '2014-12-02 06:35:38.917');
INSERT INTO #T (id, col1, col2, namecol1, datetime1, teamcol1, namecol2, datetime2) VALUES (4, 12345, 2345, 'name1', '2014-10-13 11:57:24.713', 'teamc', 'def', '2014-12-22 11:57:54.863');
INSERT INTO #T (id, col1, col2, namecol1, datetime1, teamcol1, namecol2, datetime2) VALUES (5, 12345, 2345, 'name1', '2014-10-13 11:57:24.713', 'teamd', 'efg', '2015-01-03 13:28:24.717');
WITH
CTE_Groups
AS
(
SELECT DISTINCT col1, col2
FROM #T
)
SELECT *
FROM
CTE_Groups
OUTER APPLY
(
SELECT TOP(1)
TT.namecol2 AS Team1
, TT.[datetime2] AS DateTime1
FROM #T AS TT
WHERE
TT.teamcol1 = 'teamb'
AND TT.col1 = CTE_Groups.col1
AND TT.col2 = CTE_Groups.col2
ORDER BY TT.[datetime2] DESC
) OA_teamb
OUTER APPLY
(
SELECT TOP(1)
TT.namecol2 AS Team2
, TT.[datetime2] AS DateTime2
FROM #T AS TT
WHERE
TT.teamcol1 = 'teamc'
AND TT.col1 = CTE_Groups.col1
AND TT.col2 = CTE_Groups.col2
ORDER BY TT.[datetime2] DESC
) OA_teamc
OUTER APPLY
(
SELECT TOP(1)
TT.namecol2 AS Team3
, TT.[datetime2] AS DateTime3
FROM #T AS TT
WHERE
TT.teamcol1 = 'teamd'
AND TT.col1 = CTE_Groups.col1
AND TT.col2 = CTE_Groups.col2
ORDER BY TT.[datetime2] DESC
) OA_teamd
Result set:
col1 col2 Team1 DateTime1 Team2 DateTime2 Team3 DateTime3
12345 2345 bcd 2014-12-02 06:35:38.917 def 2014-12-22 11:57:54.863 efg 2015-01-03 13:28:24.717
If there is no teamb for a certain combination of col1 and col2, there will be NULLs in Team1 and DateTime1. Same for teamc and teamd.