I have a SQL Server query which is supposed to select those first week and third week login to our portal but didn't login at second week. My problem is, the query below taking about 15 secs to be loaded. Is there any faster way or any problem on my query ?
select
count(distinct id )
from
table_x
where
g in (319, 329)
and enable = 1
and Date between '2016-01-18' and '2016-01-24' --Third Week
and id in (select distinct id
from table_x
where g in (319, 329)
and enable = 1
and Date between '2016-01-05' and '2016-01-11' --First Week
and id not in (select distinct id
from table_x
where g in (319, 329)
and enable = 1
and Date between '2016-01-11' and '2016-01-17' --Second Week
)
)
Try using conditional aggregates (a single where clause and summing 3 case expressions) instead of multiple passes through the table.
SELECT
COUNT(*)
FROM (
SELECT
user_id
, SUM(CASE WHEN [Date] BETWEEN '2016-01-18' AND '2016-01-24' THEN 1 ELSE 0 END) [ThirdWeek]
, SUM(CASE WHEN [Date] BETWEEN '2016-01-11' AND '2016-01-17' THEN 1 ELSE 0 END) [SecondWeek]
, SUM(CASE WHEN [Date] BETWEEN '2016-01-05' AND '2016-01-11' THEN 1 ELSE 0 END) [FirstWeek]
FROM table_x
WHERE x1.g IN (319, 329)
AND x1.enable = 1
AND x1.[Date] BETWEEN '2016-01-05' AND '2016-01-24'
GROUP BY
user_id
) d
WHERE [FirstWeek] > 0
AND [ThirdWeek] > 0
AND [SecondWeek] = 0
While I would expect the above to be a good option, perhaps use of EXISTS/NOT EXISTS could help, note you do NOT need distinct in the following example.
SELECT
COUNT(DISTINCT user_id)
FROM table_x x1
WHERE x1.g IN (319, 329)
AND x1.enable = 1
AND x1.[Date] BETWEEN '2016-01-18' AND '2016-01-24' --Third Week
AND EXISTS (
SELECT
NULL
FROM table_x
WHERE g IN (319, 329)
AND enable = 1
AND Date BETWEEN '2016-01-05' AND '2016-01-11' --First Week
AND x1.user_id = table_x.user_id
)
AND NOT EXISTS (
SELECT
NULL
FROM table_x
WHERE g IN (319, 329)
AND enable = 1
AND Date BETWEEN '2016-01-11' AND '2016-01-17' --Second Week
AND x1.user_id = table_x.user_id
)
;
Related
I am working on one of requirement the raw data is in following format
Requirement - Startdate should be the date when status changed to 1 and enddate should be the 1st date after the record status changed from 1 to any other number.
Customer
Status
Date
A123
0
7/2/2021
A123
0
7/15/2021
A123
0
7/22/2021
A123
1
8/18/2021
A123
1
9/8/2021
A123
0
12/1/2021
A123
0
1/21/2022
A123
1
3/6/2022
A123
1
3/7/2022
A123
0
3/15/2022
B123
1
1/1/2022
B123
0
1/6/2022
C123
1
1/2/2022
C123
2
1/8/2022
C123
0
1/9/2022
expected output
Customer
StartDate
EndDate
A123
8/18/2021
12/1/2021
A123
9/8/2021
12/1/2021
A123
3/6/2022
3/15/2022
A123
3/7/2022
3/15/2022
B123
1/1/2022
1/6/2022
C123
1/2/2022
1/8/2022
Query I tried to get the output is below, I am getting the output for Customer B123 and C123, but not for A123 as expected.
Query Explanation - In 1st part of query I am taking all the records with status = 1 and in next part taking only those records where status is not equal to 1, and joining these 2 datasets based on Customer and row number generated.
SELECT A.[Customer],A.StartDate,B.EndDate
from
(
SELECT [Customer],MIN(Date) AS STARTDATE,[Status],RANK() OVER (PARTITION BY [STATUS] ORDER BY Date ASC) AS ROWNUM
FROM table1
WHERE [STATUS] = 1
GROUP BY Customer,Date,[Status]
) A
LEFT JOIN
(
SELECT [Customer],MIN(Date) AS ENDDATE,[Status],RANK() OVER (PARTITION BY [STATUS] ORDER BY Date ASC) AS ROWNUM
FROM table1
WHERE [STATUS] != 1
AND Date>(
SELECT MIN(Date) AS STARTDATE
FROM table1
WHERE [STATUS] = 1
)
GROUP BY Customer,Date,[Status]
) B
ON
(
A.[Customer] = B.[Customer]
AND A.RowNum = B.RowNum
)
ORDER BY A.Startdate
First you list the rows where Status = 1 and then use CROSS APPLY to get the corresponding minimum Date where the Status is not equal to 1
select s.[Customer],
StartDate = s.[Date],
EndDate = e.[Date]
from Table1 s
cross apply
(
select [Date] = min(e.[Date])
from Table1 e
where e.[Customer] = s.[Customer]
and e.[Date] > s.[Date]
and e.[Status] <> 1
) e
where s.[Status] = 1
order by s.[Customer], s.[Date]
Here is a more efficient way to do this without a self-join.
WITH cte01only AS
( SELECT *, CASE Status WHEN 1 THEN 1 ELSE 0 END AS Status1 FROM table1 ),
cteDifference AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY Customer ORDER BY Date, Status1)
- ROW_NUMBER() OVER (PARTITION BY Customer, Status1 ORDER BY Date) AS StatusGroup
FROM cte01only
),
cteGroup AS
(
SELECT Customer, StatusGroup, Status1, MIN(Date) As StartDate
FROM cteDifference
GROUP BY Customer, StatusGroup, Status1
),
cteNextDate AS
(
SELECT Customer, StatusGroup, Status1, StartDate,
LEAD(StartDate, 1, NULL) OVER (PARTITION BY Customer ORDER BY StatusGroup) AS EndDate
FROM cteGroup
)
SELECT Customer, StartDate, EndDate
FROM cteNextDate
WHERE Status1 = 1
ORDER BY Customer, StateDate
The key trick here is the second CTE which uses the difference of two ROW_NUMBER() functions to tag the customer records (with the StatusGroup column) into separate partitions by contiguous runs of records whose status is 1 or not 1. After that they can be grouped according to that tag to get the start dates, and then use the LEAD() function to get the following group's StartDate as the current groupings EndDate.
(There may be a more compact way to express this, but I like to layout each stage as a separate CTE.)
When I execute the following statement:
select *
from data
where startdate >= '4/06/2018' and enddate <= '11/06/2018'
I get these results:
I would like to get this result in SQL Server:
Is this possible in SQL Server? I'm not sure if I need to use a pivot or not?
Thanks in advance!
If you don't have a calendar table or tally table, you can use an ad-hoc tally table in concert with a LEFT JOIN
Example
Declare #Date1 date = '2018-06-04'
Declare #Date2 date = '2018-06-11'
Select A.[Date]
,Val = sum(isnull(B.Val,0))
From (
Select Top (DateDiff(DAY,#Date1,#Date2)+1) [Date]=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),#Date1)
From master..spt_values n1,master..spt_values n2
) A
Left Join YourTable B
on A.[Date] >= B.StartDate and A.[Date] < B.EndDate
Group By A.[Date]
Order by A.[Date]
Returns
Date Val
2018-06-04 0
2018-06-05 2
2018-06-06 2
2018-06-07 3
2018-06-08 3
2018-06-09 0
2018-06-10 0
2018-06-11 0
I have little table which gives me a very hard time:
Person datetime1 datetime2
Eric 2012-10-01 09:00:05.000 2012-10-01 22:00:00.000
Anna 2012-10-02 06:00:05.000 2012-10-03 12:00:05.000
Richard 2012-10-03 09:00:05.000 2012-10-04 02:00:05.000
Chuck 2012-10-01 12:00:05.000 2012-10-01 23:00:05.000
I am trying to write a query, which gives me statistics table. This table contains information about when a user logged in and out (daily granularity):
Date logged_in logged_off
2012-10-01 2 2
2012-10-02 1 0
2012-10-03 1 1
2012-10-04 0 1
According to my research, a pivot command could solve the problem?
select Person,
SUM(case when datetime1 = '2012-10-01' then 1 else 0 end) as [loggeed_in],
SUM(case when datetime2 = '2012-10-01' then 1 else 0 end) as [logged_of]
from table
group by Person
This is not working... Do you have any ideas?
This will fix the current query, but don't know if it will solve the whole problem...
select Person,
SUM(case when convert(varchar(10), datetime1, 111) = '2012/10/01' then 1 else 0 end) as [loggeed_in],
SUM(case when convert(varchar(10), datetime2, 111) = '2012/10/01' then 1 else 0 end) as [logged_of]
from table
group by Person
EDIT: I believe this will better suit requirements...
SELECT
[Date] = dt,
logged_in = (
SELECT COUNT(*)
FROM table1
WHERE convert(varchar(10), datetime1, 111) = convert(varchar(10), dt, 111)),
logged_off = (
SELECT COUNT(*)
FROM table1
WHERE convert(varchar(10), datetime2, 111) = convert(varchar(10), dt, 111))
FROM (
SELECT TOP 1000
row_number() OVER(ORDER BY (SELECT 0)) AS N
FROM master.dbo.syscolumns sc1, master.dbo.syscolumns sc2) tally
CROSS APPLY(
SELECT dt = DATEADD(dd, tally.N - 1, '2012-10-1')) tallydt
WHERE dt BETWEEN (SELECT MIN(dateadd(dd, -1, datetime1)) FROM table1) AND (SELECT MAX(datetime2) FROM table1)
GROUP BY dt
ORDER BY dt
Here is the working solution:
WITH O AS (
SELECT
CAST([login Date & Time] AS DATE) loginDate
,COUNT(*) logined
FROM table
GROUP BY CAST([login Date & Time] AS DATE)
), C AS (
SELECT
CAST([Close Date & Time] AS DATE) CloseDate
,COUNT(*) Closed
FROM table
WHERE [Close Date & Time] IS NOT NULL
GROUP BY CAST([Close Date & Time] AS DATE)
)
SELECT
COALESCE(C.CloseDate, O.loginDate) TheDate
--,O.loginDate
--,C.CloseDate
,O.logined
,C.Closed
FROM O
FULL JOIN C
ON O.loginDate = C.CloseDate
ORDER BY TheDate
Is it possible to write single query for following scenario?
Scenario -
Table -
column name - id date isPaid
values - 1 1/1/2011 1
2 1/2/2011 1
3 1/3/2011 0
4 1/4/2011 0
5 1/5/2011 0
I want a result set which contains (all ispaid = 1) and (only 1 row of ispaid = 0 whose date is smaller).
Result set:
column name - id date isPaid
values - 1 1/1/2011 1
2 1/2/2011 1
3 1/3/2011 0
Thanks
You can use UNIONdocs
SELECT
[id],
[date],
[isPaid]
FROM
[tablename]
WHERE
[ispaid] = 1
UNION ALL
SELECT TOP 1
[id],
[date],
[isPaid]
FROM
[tablename]
WHERE
[ispaid] = 0
ORDER BY
[date] ASC
This should do what you need in SQL Server 2005 and higher.
select
[id],
[date],
isPaid
from (
select
[id],
[date],
isPaid,
ROW_NUMBER() over (partition by ispaid order by date) as row
from table_name t ) a
where ispaid = 1
or row = 1
order by [date]
Assuming date will be provided to the query by user, for which data is to be retrieved
select t.*
from table t,
(select Top 1 id, date, ispaid
from table
where ispaid = 0 and date<?) np
where (t.ispaid=1 and t.date = ? ) OR (t.id = np.id)
Table :tbl_user
dateofregistration ID registrationstate
6-03-11 3 0
6-03-11 1 0
6-03-11 2 1
7-03-11 2 1
7-03-11 1 1
how can I display result like this for sql server 2008 express
date TotalID(count) Total State(0 only)
6-03-11 3 2
7-03-11 2 0
I have tried with this
SELECT CONVERT(varchar(10), dateofregistration, 103) AS Date,
(select COUNT(ID)) AS Subbase,
(Select Count(ID)from tbl_User where (registrationstate='0')) AS Totalchurn
FROM tbl_User
GROUP BY CONVERT(varchar(10), dateofregistration, 103);
but wrong result.Any help plz.
How about;
select
cast(dateofregistration as date),
count(distinct id), --or * for all
sum(
case registrationstate when '0' then 1 else 0 end
)
from tbl_user
group by cast(dateofregistration as date)
order by 1
2011-06-03 3 2
2011-07-03 2 0
SELECT CONVERT(varchar(10), dateofregistration, 103) AS Date,
COUNT(1) AS Subbase,
SUM(CASE WHEN registrationstate='0' THEN 1 ELSE 0 END) AS Totalchurn
FROM tbl_User
GROUP BY CONVERT(varchar(10), dateofregistration, 103)
ORDER BY 1
You were nearly there. You don't need a subselect for COUNT(ID) since that is handled by the GROUP BY. You group by date, and so the count will be the number of IDs within each date. I've made the count distinct, just in case you can have multiple registrations of the same ID on one day.
Your subquery was almost right - it needs to be correlated with the main query by selecting rows with the same registration date.
SELECT CONVERT(varchar(10), dateofregistration, 103) AS Date,
COUNT(DISTINCT ID) AS TotalID,
(Select Count(*) from tbl_User t2 where (registrationstate='0') AND t2.registrationdate=t1.registrationdate) AS Totalchurn
FROM tbl_User t1
GROUP BY CONVERT(varchar(10), dateofregistration, 103);