Select and group by Year, Month and Condition - sql

I have a table like this (this is really an example only):
+-------------+---------------------+---------------------+
| status | open_date | close_date |
+-------------+---------------------+---------------------+
| closed | 01-11-2014 19:32:44 | 01-11-2014 20:32:44 |
| open | 01-12-2014 22:33:49 | 02-12-2014 22:33:49 |
| open | 01-23-2014 22:08:24 | 03-23-2014 22:08:24 |
| closed | 02-01-2014 22:33:57 | 03-01-2014 22:33:57 |
| open | 02-01-2013 22:37:34 | 02-01-2013 23:37:34 |
| closed | 04-20-2013 15:23:00 | 05-20-2013 15:23:00 |
| open | 04-20-2013 12:21:49 | 05-20-2013 12:21:49 |
| closed | 04-25-2013 11:22:00 | 06-25-2013 11:22:00 |
| closed | 05-20-2013 14:23:49 | 10-20-2013 14:23:49 |
| closed | 04-20-2013 16:33:49 | 04-25-2013 16:33:49 |
+-------------+---------------------+---------------------*
And want to list all opened and closed cases by Year and Month, like this:
+-------------+---------------+--------------+
| Year | Month | Opened Cases | Closed Cases |
+-------------+---------------+--------------+
| 2014 | 4 | 10 | 5 |
| 2014 | 3 | 9 | 7 |
| 2014 | 2 | 15 | 10 |
| 2014 | 1 | 12 | 1 |
| 2013 | 12 | 30 | 9 |
| 2013 | 11 | 5 | 50 |
+--------------+--------------+--------------+
I have a select like this:
SELECT
YEAR(open_date) AS TheYear,
MONTH(open_date) AS TheMonth,
sum(CASE WHEN open_date = ??? THEN 1 ELSE 0 END) TheOpened
sum(CASE WHEN close_date = ??? THEN 1 ELSE 0 END) TheClosed
FROM
TABLE
WHERE
CASEGROUP= 'SUPPORT'
GROUP BY
MONTH(open_date),
YEAR(open_date)
ORDER BY
TheYear DESC,
TheMonth ASC

Try this
SELECT
YEAR(open_date) AS TheYear,
MONTH(open_date) AS TheMonth,
sum(CASE WHEN open_status= 'Closed' THEN 0 ELSE 1 END) TheOpened,
sum(CASE WHEN open_status= 'Closed' THEN 1 ELSE 0 END) TheClosed
FROM
TABLE
WHERE
CASEGROUP= 'SUPPORT'
GROUP BY
MONTH(open_date),
YEAR(open_date)
ORDER BY
TheYear DESC,
TheMonth ASC

select [Year], [Month], Sum([Opened Cases]), sum([Closed Cases]) from (
select year(open_date) as [Year], MONTH(open_date) as [Month], count(*) as [Opened Cases] ,0 as [Closed Cases]
from [table] where status='open'
group by year(open_date), MONTH(open_date)
union
select year(close_date), MONTH(close_date), 0,count(*)
from [table] where status='closed'
group by year(close_date), MONTH(close_date)) as o
group by [Year], [Month]

Found it.
SELECT
YEAR(open_time) AS TheYear,
MONTH(open_time) AS TheMonth,
sum(CASE WHEN DATEPART(YYYY, open_time)= YEAR(open_time) AND DATEPART(MM, open_time)= MONTH(open_time) THEN 1 ELSE 0 END) TheOpened,
sum(CASE WHEN status = 'closed' THEN 1 ELSE 0 END) TheClosed
FROM
TABLE
WHERE
CASEGROUP= 'SUPPORT'
GROUP BY
MONTH(open_date),
YEAR(open_date)
ORDER BY
TheYear DESC,
TheMonth ASC

Related

Count item after joining two table

I have two tables,
First is Product table
+-----------+------+-----+------------+
| id | pnum | year |month|date |
+-----------+------+-----+------------+
|12 | S5 | 2021 | 2 | 2021-02-21 |
|12 | S5 | 2021 | 2 | 2021-02-22 |
|12 | S5 | 2021 | 2 | 2021-02-23 |
|33 | A55| 2021 | 3 | 2021-03-01 |
|44 | B1 | 2021 | 6 | 2021-06-01 |
Second is Deal table
+-----------+------+-----+------------+
| id | pnum| year |month|date |
+-----------+------+-----+------------+
|12 | S5 | 2021 | 2 | 2021-02-28 |
|12 | S5 | 2021 | 2 | 2021-02-01 |
|33 | A55| 2021 | 3 | 2021-03-01 |
I need a result which can tell me how many product got launch
for a year_month and count of deal in first 15 days or in next 15 days
+----------- +------------+----------------+--------------------+
| num | count| year-month |deal_in_first15 |deal_after_first15 |
+----------- +------+-----+-------------------------------------
|S5 | 3 | 2021-02 | 1 | 1 |
|A55 | 1 | 2021-03 | 1 | 0 |
I was trying to do it like below
select * from Product p inner join Deal d on
p.num=d.num AND p.id=d.id AND p.month=d.month
but it is not helping me to get exact result as intended.
I have some java and python background and not expert in sql hence applying count and case statement is not working out.
You can try to use the condition aggregate function in subquery then do JOIN
select p.pnum ,
COUNT(*) count,
FORMAT(p.[date],'yyyy-MM') 'year-month',
deal_in_first15 ,
deal_after_first15
from Product p
inner join (
SELECT id ,
pnum ,
month,
year ,
COUNT(CASE WHEN DATEPART(day,[date]) < 15 THEN 1 END) deal_in_first15 ,
COUNT(CASE WHEN DATEPART(day,[date]) >= 15 THEN 1 END) deal_after_first15
FROM Deal
GROUP BY id ,pnum ,month,year
) d on
p.pnum=d.pnum AND p.id=d.id AND p.month=d.month
group by FORMAT(p.[date],'yyyy-MM') ,
p.pnum,
deal_in_first15 ,
deal_after_first15
I think there is another way might you want, using two subquery then JOIN
select p.pnum ,
p.cnt 'count',
CONCAT(p.year,'-',FORMAT(p.month,'0#')) 'year-month',
deal_in_first15 ,
deal_after_first15
from (
SELECT id ,
pnum ,
month,
year,
count(*) cnt
FROM Product
GROUP BY id ,
pnum ,
month,
year
) p
inner join (
SELECT id ,
pnum ,
month,
year ,
COUNT(CASE WHEN DATEPART(day,[date]) < 15 THEN 1 END) deal_in_first15 ,
COUNT(CASE WHEN DATEPART(day,[date]) >= 15 THEN 1 END) deal_after_first15
FROM Deal
GROUP BY id ,pnum ,month,year
) d on
p.pnum=d.pnum
AND p.id=d.id
AND p.month=d.month
AND p.year=d.year
sqlfiddle
Note
I would use the year column to be JOIN condition. otherwise, the result will be wrong when you cross-year meet the same month.
This is it:
select pnum,cnt,sum(mn1),sum(mn2) from (select d."pnum",(select count(*) from Product p where p."pnum"=d."pnum"
) cnt,
case when
EXTRACT(DAY FROM d."date")<=15 then sum(1) else sum(0) end mn1 ,
case when
EXTRACT(DAY FROM d."date")>15 then sum(1) else sum(0) end mn2,
d."year"||'-'||d."month"
from deal d
group by 1,d."year"||'-'||d."month",d."date")abc group by 1,2;
please check at:http://sqlfiddle.com/#!17/3bad9/18

How to count both all the records and part of records based on a condition in the same query?

i try in sql, use this command
SELECT year(datecreated) as tahun, jenis_kelamin, COUNT(jenis_kelamin) as jumlah
from santri
GROUP by jenis_kelamin, tahun
but it show like this
+------+-------------+-----------+
| Tahun|jenis_kelamin| JUmlah |
+------+-------------+-----------+
| 2009 | Laki_laki| 5 |
+------+-------------+-----------+
|2010 | Perempuan | 7 |
+------+-------------+-----------+
|2010 | laki-laki | 6 |
+------+-------------+-----------+
i want view in sql like this
+------+-----------+-----------+-----+
| Tahun| laki-laki | perempuan |Jumlah
+------+-----------+-----------+-----+
| 2009 | 5 | 0 | 5 |
+------+-----------+-----------+-----+
| 2 | 6 | 7 | 13 |
+-----+------------+-----------+-----+
i try to modificated my commad but still not alike this
You seem to want conditional aggregation:
SELECT YEAR(datecreated) as tahun,
SUM(CASE WHEN jenis_kelamin = 'laki-laki' THEN 1 ELSE 0 END) as lakilaki,
SUM(CASE WHEN jenis_kelamin = 'Perempuan' THEN 1 ELSE 0 END) as Perempuan,
COUNT(*) as jumlah
from santri
GROUP by tahun;
I am interpreting the tahun value in the second row as a typo.

SQL-Server query to select last and previous information for multiple columns

After looking in Stackoverflow I cant find a solution to this problem.
I'm using this query:
SELECT *
FROM(
SELECT DISTINCT *
FROM Table_01
ORDER BY ID, StartDate
UNION ALL(
SELECT DISTINCT * FROM Table_02
ORDER BY ID, StartDate
)
UNION ALL (...
) a ORDER BY a.ID, a.StartDate
I got something like this, for each ID i would like to keep the last and previous date and other columns, to record a history
+------+------------+-----------+-------+-------+
| ID | StartDate | EndDate | Value | rate |
+------+------------+-----------+-------+-------+
| 1 | 2018-06-29 |2018-10-22 | 15 | 77.2 |
| 1 | 2018-04-28 |2018-06-21 | 23 | 55.3 |
| 1 | 2018-02-24 |2018-04-15 | 41 | 44.3 |
| 1 | 2017-06-29 |2017-11-29 | 55 | 44.1 |
| 2 | 2018-07-29 |2018-11-22 | 15 | 106.1 |
| 2 | 2018-03-28 |2018-07-21 | 23 | 10.8 |
| 2 | 2017-12-28 |2018-03-28 | 22 | 11.0 |
| 3 | 2017-09-28 |2018-01-28 | 11 | 87.09 |
| 3 | 2017-06-27 |2018-09-28 | 58 | 100 |
| ... | ... | ... | ... | ... |
+------+------------+-----------+-------+--------+
And I would like to have the next table, to keep the previous information
+------+------------+-----------+------------+-----------+-------+--------+-------+--------+
| ID | StartDate | EndDate | StartDateP | EndDateP | Value | rate | ValueP| rateP |
+------+------------+------------+-----------+-----------+-------+--------+-------+--------+
| 1 | 2018-06-29 |2018-10-22 | 2018-04-28 |2018-06-21 | 15 | 77.2 | 23 | 55.3 |
| 2 | 2018-07-29 |2018-11-22 | 2018-03-28 |2018-07-21 | 15 | 106.1 | 23 | 10.8 |
| 3 | 2017-09-28 |2018-01-28 | 2017-06-27 |2018-09-28 | 11 | 87.09 | 58 | 100 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
+------+------------+-----------+------------+-----------+-------+--------+-------+--------+
If I understand you correctly you want the row with the latest start date combined with the row with the startdate just before that? This might do the trick
WITH results AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY StartDate DESC) r
FROM (
-- start of your original query
SELECT DISTINCT *
FROM Table_01
ORDER BY ID, StartDate
UNION ALL
(
SELECT DISTINCT *
FROM Table_02
ORDER BY ID, StartDate
)
UNION ALL
(...) a
ORDER BY a.ID, a.StartDate
-- end of your original query
)
)
SELECT
r1.id, r1.startDate, r2.enddate,
r2.startDate startDateP, r2.enddate enddateP,
r1.value, r1.rate,
r2.value valueP, r2.rate rateP
FROM results r1
LEFT JOIN results r2 ON r2.id = r1.id AND r2.r = 2
WHERE r1.r = 1
Another option is using Row_Number() in concert with a conditional aggregation
Example
Select ID
,StartDate = max(case when RN=1 then StartDate end)
,EndDate = max(case when RN=1 then EndDate end)
,StartDateP = max(case when RN=2 then StartDate end)
,EndDateP = max(case when RN=2 then EndDate end)
,Value = max(case when RN=1 then Value end)
,Rate = max(case when RN=1 then Rate end)
,ValueP = max(case when RN=2 then Value end)
,RateP = max(case when RN=2 then Rate end)
From (
Select *
,RN = Row_Number() over (Partition By ID Order by EndDate Desc)
From YourTable
) A
Group By ID
Returns

Multi-part identifier issue and pivoting data in two ways on a single table/view

I have created a view in SQL that gives me the following data:
+------+--------+--------+------+------+
| Year | Issuer | PNL | LgSt | PPNL |
+------+--------+--------+------+------+
| 2017 | a | 47433 | L | 1 |
| 2016 | b | -29250 | S | 0 |
| 2017 | c | 211469 | S | 1 |
| 2016 | d | 8231 | S | 1 |
| 2018 | a | 89679 | S | 1 |
| 2018 | b | -34655 | L | 0 |
| 2017 | b | 65035 | L | 1 |
| 2017 | c | -52719 | L | 0 |
| 2015 | a | 11621 | L | 1 |
+------+--------+--------+------+------+
The code for the view is:
CREATE VIEW
[Portfolio].[HitRatePretable]
AS
WITH CTE1
AS
(
SELECT DP.Issuer, DP.AssetCategory, SUM (DP.GLPeriod) As [PNL],
SUM(DP.DeltaExpNet) NetExp, SUM(DP.DeltaExpGross) GrossExp, DP.Date,
DATEPART (YYYY,DP.date) As [Year]
FROM Portfolio.DailyPortfolio DP
GROUP BY Issuer,DP.AssetCategory ,DP.Date
)
,
CTE2
AS
(
SELECT CTE1.Year, CTE1.Issuer, SUM(CTE1.PNL) [PNL],
CASE
WHEN ABS(MAX(CTE1.NetExp)) > ABS(MIN(CTE1.NetExp)) THEN 'L'
ELSE 'S'
END As [LS]
FROM CTE1
GROUP BY CTE1.Year, CTE1.Issuer
HAVING SUM(CTE1.PNL) >1000
OR SUM(CTE1.PNL)<-1000
)
SELECT CTE2. Year, CTE2.Issuer,CTE2.PNL[PNL],CTE2.LS [LgSt],
CASE
WHEN CTE2.PNL>0 THEN 1
ELSE 0
END As [PPNL]
FROM CTE2
I need to create another view from this data that looks like this:
+------+---------------+------------+---------------+------------+---------------+------------+---------------+------------+
| LgSt | 2015 | 2015Total | 2016 | 2016Total | 2017 | 2017Total | 2018 | 2018Total |
+------+---------------+------------+---------------+------------+---------------+------------+---------------+------------+
| L | Sum of (PPNL) | Count of L | Sum of (PPNL) | Count of L | Sum of (PPNL) | Count of L | Sum of (PPNL) | Count of L |
| S | Sum of (PPNL) | Count of S | Sum of (PPNL) | Count of S | Sum of (PPNL) | Count of S | Sum of (PPNL) | Count of S |
+------+---------------+------------+---------------+------------+---------------+------------+---------------+------------+
Before I even attempted that, I tried to do a simpler pivot that would return the data in this format:
+------+---------------+---------------+---------------+---------------+
| LgSt | 2015 | 2016 | 2017 | 2018 |
+------+---------------+---------------+---------------+---------------+
| L | Sum of (PPNL) | Sum of (PPNL) | Sum of (PPNL) | Sum of (PPNL) |
| S | Sum of (PPNL) | Sum of (PPNL) | Sum of (PPNL) | Sum of (PPNL) |
+------+---------------+---------------+---------------+---------------+
My code for the simpler pivot was:
select HR.LgSt, [2015],[2016],[2017],[2018]
from portfolio.HitRatePretable HR
Pivot
(
SUM (PPNL) FOR LgSt IN
( [2015],[2016],[2017],[2018])
) AS pvt
This returned the following error:
Msg 4104, Level 16, State 1, Line 1
The multi-part identifier "HR.LgSt" could not be bound.
My questions are:
Why did I get that error?
How do I create a view with the Total columns?
Thanks.
To get the SUM and Total, I will go with CASE in a single query like below.
select LgSt,
SUM(CASE WHEN YEAR=2015 THEN PPNL ELSE 0 END) as [2015],
SUM(CASE WHEN YEAR=2015 THEN 1 ELSE 0 END) as [2015]Total,
SUM(CASE WHEN YEAR=2016 THEN PPNL ELSE 0 END) as [2016],
SUM(CASE WHEN YEAR=2016 THEN 1 ELSE 0 END) as [2016]Total,
SUM(CASE WHEN YEAR=2017 THEN PPNL ELSE 0 END) as [2017],
SUM(CASE WHEN YEAR=2017 THEN 1 ELSE 0 END) as [2017]Total,
SUM(CASE WHEN YEAR=2018 THEN PPNL ELSE 0 END) as [2018],
SUM(CASE WHEN YEAR=2018 THEN 1 ELSE 0 END) as [2018]Total
from portfolio.HitRatePretable HR
Group by LgSt
Try:
select pvt.LgSt, [2015],[2016],[2017],[2018]
from portfolio.HitRatePretable HR
Pivot
(
SUM (PPNL) FOR Year IN
( [2015],[2016],[2017],[2018])
) AS pvt
You want to pivot on Year, not LgSt

SQL Server 2008 How can I SELECT rows with Value by GROUP(ing) other column?

Hi guys I'm a beginner at SQL so please bear with me.. :)
My question is as follows.
I got this table:
DateTime ID Year Month Value Cost
-------------------|------|--------|-------|-------|--------|
1-1-2013 00:00:01 | 1 | 2013 | 1 | 30 | 90 |
1-1-2013 00:01:01 | 1 | 2013 | 1 | 0 | 0 |
1-1-2013 00:02:01 | 1 | 2013 | 1 | 1 | 3 |
1-2-2013 00:00:01 | 1 | 2013 | 2 | 2 | 6 |
1-2-2013 00:01:01 | 1 | 2013 | 2 | 3 | 9 |
1-2-2013 00:02:01 | 1 | 2013 | 2 | 4 | 12 |
1-3-2013 00:00:01 | 1 | 2013 | 3 | 5 | 15 |
1-3-2013 00:01:01 | 1 | 2013 | 3 | 6 | 18 |
1-3-2013 00:02:01 | 1 | 2013 | 3 | 7 | 21 |
Now what I'm trying to get is this result
Year Month Value Cost
|--------|-------|-------|--------|
| 2013 | 1 | 1 | 3 |
| 2013 | 2 | 4 | 12 |
| 2013 | 3 | 7 | 21 |
As you can see I'm trying to GROUP BY the [Month] and the [Year] and to get the last [Value] for every [Month].
Now as you can understand from the result I do not try to get the MAX() value from the [Value] column but the last value for every [Month] and that is my issue..
Thanks in advance
PS
I was able to GROUP BY the [Year] and the [Month] but as I understand that when I adding the [Value] column the GROUP BY is not effecting the result, as the SQL need more spcification on the value you what the SQL to get..
Instead of using row_number(), you can also use rank(). Using rank() might give you multiple values within the same year and month, see this post.
Because of this, a group by is added.
SELECT
[Year],
[Month],
[Value],
[Cost]
FROM
(
SELECT
[Year],
[Month],
[Value],
[Cost],
Rank() OVER (PARTITION BY [Year], [Month] ORDER BY [DateTime] DESC) AS [Rank]
FROM [t1]
) AS [sub]
WHERE [Rank] = 1
GROUP BY
[Year],
[Month],
[Value],
[Cost]
ORDER BY
[Year] ASC,
[Month] ASC
As stated in the comments, this might still return multiple records for a single month. Therefor the ORDER BY statement can be extended, based on the desired functionality:
Rank() OVER (PARTITION BY [Year], [Month] ORDER BY [DateTime] DESC, [Value] DESC, [Cost] ASC) AS [Rank]
Switching the order of [Value] and [Cost] or ASC <> DESC will influence the rank and because of that the result.
Since you are using SQL Server 2008, you can use row_number() to get the result:
select year, month, value, cost
from
(
select year, month, value, cost,
row_number() over(partition by year, month order by datetime desc) rn
from yourtable
) src
where rn = 1
See SQL Fiddle with Demo
Or you can use a subquery to get this (note: with this version if you have more than one record with the same max datetime per month then you will return each record:
select t1.year, t1.month, t1.value, t1.cost
from yourtable t1
inner join
(
select max(datetime) datetime
from yourtable
group by year, month
) t2
on t1.datetime = t2.datetime
See SQL Fiddle with Demo
Both give the same result:
| YEAR | MONTH | VALUE | COST |
-------------------------------
| 2013 | 1 | 1 | 3 |
| 2013 | 2 | 4 | 12 |
| 2013 | 3 | 7 | 21 |