How do I count a Date Column based on another Date Column? - sql

What I'm trying to achieve is to count the login attempts of a user based on the LoginAttempts value and on the LastLoginDate. For example, I need to query the LastLoginDate within 30 days with 2 Loginattempts.
result should be:
What I have is this..I created temp table to pull the information and and it doesn't seem to be counting correctly. Here's where I'm stuck..Any help would be appreciated!!

Your GROUP BY is incorrect. It includes LastLoginDateUTC. Consequently, you're counting logins per date, not logins per 30 days.
Drop LastLoginDateUTC from your GROUP BY, and change the SELECT clause to use max(LastLoginDateUTC) as LastLoginDateUTC. That should give you what you want.

Not sure if I understand your request completely, but here's something outside the box. Good luck!:
select FirstName, StudentID, UserName, LastLoginDate, LoginAttempts,
RowNumber = row_number() over(partition by StudentID order by StudentID, LoginAttempts)
into #temp1
from user
where cast(LastLoginDate as date) >= getdate()- 30
--should return all rows of data for past 30 days.
--should also rank each loginAttempt by student
select * from #temp1
where RowNumber >= 3

This not an answer... more a suggestion.
As I see it you have Students and you need to audit login attempts.
This for me is a one to many relation.
So I would keep the Student table, stripped off from any login related data.
This would simplify your Student table, keep less rows and save storage space for not using the same data (name, username, etc) over and over again.
That data would be in a StudentLoginAttempts, something like:
Create Table StudentLoginAttempts (
Id int not null identity(1,1),
StudentId int not null,
LoginDate datetime not null,
Successful bit not null,
Constraint PK_StudentLoginAttempts Primary Key Clustered (Id),
Constraint FK_StudentLoginAttempts_Student Foreign Key (StudentId) References Student(StudentId)
)
go
Create Index IX_StudentLoginAttempts_StudentId On StudentLoginAttempts(StudentId)
go
Create Index IX_StudentLoginAttempts_LoginDate On StudentLoginAttempts(LoginDate)
go
So things could be more clear and you can have more info.
Think the example bellow:
Create Table #Student (
StudentId int not null identity(1,1),
Username varchar(50) not null,
FirstName varchar(50) not null
)
Create Table #StudentLoginAttempts (
Id int not null identity(1,1),
StudentId int not null,
LoginDate datetime not null,
Successful bit not null
)
insert into #Student values
( 'Student001', 'JON' ),
( 'Student002', 'STEVE' )
insert into #StudentLoginAttempts values
( 1, '2016-01-01 09:12', 0 ),
( 1, '2016-02-01 09:12', 0 ),
( 1, '2016-03-01 09:12', 1 ),
( 2, '2016-03-02 10:12', 0 ),
( 2, '2016-04-02 10:12', 1 ),
( 2, '2016-05-02 10:12', 0 )
;with TotalAttemptsCte as (
select StudentId, TotalLoginAttempts = count(*) from #StudentLoginAttempts group by StudentId
),
FailedCte as (
select StudentId, FailedLogins = count(*) from #StudentLoginAttempts where ( Successful = 0 ) group by StudentId
),
SuccessfulCte as (
select StudentId, SuccessfulLogins = count(*) from #StudentLoginAttempts where ( Successful = 1 ) group by StudentId
),
LastSuccessFulDateCte as (
select StudentId, max(LoginDate) as LastSuccessfulLoginDate
from
#StudentLoginAttempts
where
( Successful = 1 )
group by StudentId
)
select
a.*, b.TotalLoginAttempts, c.FailedLogins, d.SuccessfulLogins, e.LastSuccessfulLoginDate
from
#Student a
left join TotalAttemptsCte b on ( a.StudentId = b.StudentId )
left join FailedCte c on ( a.StudentId = c.StudentId )
left join SuccessfulCte d on ( a.StudentId = d.StudentId )
left join LastSuccessFulDateCte e on ( a.StudentId = e.StudentId )
Drop Table #StudentLoginAttempts
Drop Table #Student
You could also create a view based on the query for more easy access.
I remind you that this is just a suggestion, how I would do it.

Related

How to query for delete the same data out of the table?

I want to delete it for the output to look like this. In one group there should be only 1 user_id.
select distinct group_id, user_id, count(*)
from pin_users
where group_id in (select group_id from pin_users)
group by group_id, user_id
having count(*) > 1
I get all user_id, group_id and count more than 1 but I don't know how to delete duplicates and leave only 1 left.
Ps. My English is probably not perfect, pls excuse any mistakes
Make a subquery to get a list of minimum ids for any combination of users and groups. Then remove everything else.
DELETE FROM pin_users WHERE id NOT IN (
SELECT min(id) as id
FROM pin_users
GROUP BY group_id, user_id
)
In case of sql-server, try this:
-- mockup data
declare #Raw table (
id nvarchar(max),
group_id nvarchar(max),
user_id nvarchar(max)
)
insert into #Raw values ('p001', 'aaa', 'ava001'), ('p002', 'aaa', 'ava001'), ('p003', 'bbb', 'ava001'), ('p004', 'bbb', 'ava001');
-- check this query
with A as (
select id, ROW_NUMBER() over(partition by group_id, user_id order by id) as RN
from #Raw)
delete from #Raw where id in (select id from A where A.RN > 1)
-- verify table
select * from #Raw
For example, with sql server should be something like this
delete u1
from pin_users u1
join (
select min(id) id, group_id, user_id
from pin_users
group by group_id, user_id
) u2
on u1.group_id = u2.group_id
and u1.user_id = u2.user_id
and u1.id <> u2.id

How to get last record from Master-Details tables

I have a table that has 3 columns.
create table myTable
(
ID int Primary key,
Detail_ID int references myTable(ID) null, -- reference to self
Master_Value varchar(50) -- references to master table
)
this table has the follow records:
insert into myTable select 100,null,'aaaa'
insert into myTable select 101,100,'aaaa'
insert into myTable select 102,101,'aaaa'
insert into myTable select 103,102,'aaaa' ---> last record
insert into myTable select 200,null,'bbbb'
insert into myTable select 201,200,'bbbb'
insert into myTable select 202,201,'bbbb' ---> last record
the records is saved In the form of relational with ID and Detail_ID columns.
I need to select the last record each Master_Value column. follow output:
lastRecordID Master_Value Path
202 bbbb 200=>201=>202
103 aaaa 100=>101=>102=>103
tips:
The records are not listed in order in the table.
I can not use the max(ID) keyword. beacuse data is not sorted.(may
be the id column updated manually.)
attempts:
I was able to Prepare follow query and is working well:
with Q as
(
select ID ,Detail_ID, Master_Value , 1 RowOrder, CAST(id as varchar(max)) [Path] from myTable where Detail_ID is null
union all
select R.id,R.Detail_ID , r.Master_Value , (q.RowOrder + 1) RowOrder , (q.[Path]+'=>'+CAST(r.id as varchar(max))) [Path] from myTable R inner join Q ON Q.ID=R.Detail_ID --where r.Dom_ID_RowType=1010
)
select * into #q from Q
select Master_Value, MAX(RowOrder) lastRecord into #temp from #Q group by Master_Value
select
q.ID lastRecordID,
q.Master_Value,
q.[Path]
from #temp t
join #q q on q.RowOrder = t.lastRecord
where
q.Master_Value = t.Master_Value
but I need to simple way (one select) and optimal method.
Can anyone help me?
One method uses a correlated subquery to get the last value (which is how I interpreted your question):
select t.*
from mytable t
where not exists (select 1
from mytable t2
where t2.master_value = t.master_value and
t2.id = t.detail_id
);
This returns rows that are not referred to by another row.
For the path, you need a recursive CTE:
with cte as (
select master_value, id as first_id, id as child_id, convert(varchar(max), id) as path, 1 as lev
from mytable t
where detail_id is null
union all
select cte.master_value, cte.first_id, t.id, concat(path, '->', t.id), lev + 1
from cte join
mytable t
on t.detail_id = cte.child_id and t.master_value = cte.master_value
)
select cte.*
from (select cte.*, max(lev) over (partition by master_value) as max_lev
from cte
) cte
where max_lev = lev
Here is a db<>fiddle.

Unable to use nonequi join(<=) in hive

I am new to hive and was trying to execute an query which contains nonequi join.
But when I try to execute in hive 0.7 I am getting error due to nonequi join condition.
Query:
SELECT * INTO dbo.tbl_B
FROM ( select
Id,
Name,
Age,
SellingPrice,
row_number() OVER ( PARTITION BY Id,Name
ORDER BY Age asc) AS row_no
from tbl_A
LEFT JOIN dbo.tbl_C
ON A.[Id]=C.[Id]
and A.SellingPrice <= C.BuyingPrice
) AA
WHERE row_no = 1 ;
Please provide me idea to rewrite this query without using nonequi join.
Move non-equi join condition to the where clause:
SELECT *
FROM ( select
Id,
Name,
Age,
SellingPrice,
row_number() OVER ( PARTITION BY Id,Name ORDER BY Age asc) AS row_no
from tbl_A
LEFT JOIN dbo.tbl_C
ON A.[Id]=C.[Id]
where (A.SellingPrice <= C.BuyingPrice) OR (C.BuyingPrice is null)
) AA
WHERE row_no = 1 ;
OR (C.BuyingPrice is null) is necessary to allow left join, without this condition, left join will be transformed to inner join because non-equality condition alone as is will filter out nulls, see here.
Solution not working for me
query used:
CREATE TABLE IMART_PREP.TST (STRT_DT DATE , END_DT DATE, ID INT , SALARY INT );
INSERT INTO IMART_PREP.TST VALUES ( '2018-01-01' , '2018-01-31' ,1, 1000);
INSERT INTO IMART_PREP.TST VALUES ( '2018-02-01' , '2018-02-28' , 1, 2000);
INSERT INTO IMART_PREP.TST VALUES ( '2018-03-01' , '2018-03-30' , 1, 3000);
INSERT INTO IMART_PREP.TST VALUES ( '2018-04-01' , '2018-04-30' , 1, 4000);
SELECT * FROM IMART_PREP.TST;
CREATE TABLE IMART_PREP.TST_1 (STRT_DT DATE , END_DT DATE, ID INT , SALARY INT );
INSERT INTO IMART_PREP.TST_1 VALUES ( '2018-01-01' , '2018-01-31', 1, 1000);
SELECT *
FROM IMART_PREP.TST A , IMART_PREP.TST_1 B
WHERE ( (A.ID = B.ID ) AND (A.STRT_DT BETWEEN B.STRT_DT AND B.END_DT ) ) OR (B.STRT_DT IS NULL );

How to fetch records from two tables without common column using CTE

Users table details
userid values (abc,xyz,abc,sdf)
master table details
(mid,priority)values(101,1),(102,2),(101,1),(103,1)
i need to count of mid based on userid (userid is names of users) group by priority(priority is int ) grouping like case priority =1 then 'Open', priority =2 then 'closed' etc using CTE(common table expression)
Select * from users
userid
abc
xyz
abc
sdf
Select * from master
mid Priority
101 1
102 2
101 1
103 1
(Priority 1= Open 2=Closed)
OUTPUT expected:
Userid count(mid) Priority
abc 2 Open
xyz 1 Closed
sdf 1 Open
Try this:
use db_test;
go
drop table dbo.users;
create table dbo.users
(
userid varchar(max) not null
)
;
insert into dbo.users
values
('abc'),
('xyz'),
('sdf')
create table dbo.master
(
mid int not null,
Priority int not null
)
;
insert into dbo.master
values
(101, 1),
(102, 2),
(101, 1),
(103, 1)
;
with cte1 as (
select userid, row_number() over(order by userid asc) as rn
from dbo.users
), cte2 as (
select mid, priority, dense_rank() over(order by mid asc) as rn
from dbo.master
)
select a.userid, count(*) as [count(mid)], b.priority
from cte1 a join cte2 b on a.rn = b.rn
group by a.userid, b.priority

SELECT values from table with grouped id but differences in non-grouped columns

I'm having trouble achieving something that seems like it should be simple. The example below shows people enrolled in classes, and a person can be enrolled in multiple classes, but only one currently:
DECLARE #Test TABLE
(
PersonId int NOT NULL,
LocationId int NOT NULL,
ClassId int NOT NULL,
IsEnrolled bit NOT NULL,
IsExited bit NOT NULL
)
Add Data:
INSERT INTO #Test
SELECT 1,5,6,1,0
UNION SELECT 1,6,7,0,1
UNION SELECT 2,5,8,1,0
UNION SELECT 2,5,9,0,1
UNION SELECT 3,5,9,0,1
UNION SELECT 3,6,9,1,0
I want to get all the records (current or not) for the people where all of their enrollment is at the same location (having the same LocationId), but only the values that are current (IsEnrolled = 1) where the locations are different.
For the same PersonId I'd like all the records if the LocationId unique, and only the current (IsEnrolled = 1) if the LocationId is not unique for the PersonId.
Data I want to get back from a query:
SELECT
1 AS PersonId,
6 AS ClassId,
1 As IsEnrolled,
0 AS IsExited
UNION SELECT 2, 8, 1, 0
UNION SELECT 2, 9, 0, 1
UNION SELECT 3, 9, 1, 0
I think it's a lot simpler than previously tried methods:
SELECT Test.*
FROM #Test Test
JOIN
(
SELECT PersonID, LocationID
FROM #Test T
WHERE ISENROLLED = 1
GROUP BY PeronID, LocationID
) T
ON Test.PersonID = T.PersonID
AND Test.LocationID = T.LocationID
Since you can only have one "isenrolled" record per person, the inner query is guaranteed to return one person/location combination for each person. Thus, joining to it on person and location ensures that you get every record for that person which was at the location of their currently enrolled class.
If I understand correctly you want all records that are either current or belong to a person who has always been in the same location. Hence:
select personid, classid, isenrolled, isexited
from mytable
where isenrolled = 1
or personid in
(
select personid
from mytable
group by personid
having min(locationid) = max(locationid)
)
order by personid, classid;
The same with window functions, so the table has to be read just once:
select personid, classid, isenrolled, isexited
from
(
select
personid, classid, isenrolled, isexited,
min(locationid) over (partition by personid) as minloc,
max(locationid) over (partition by personid) as maxloc
from mytable
)
where isenrolled = 1 or minloc = maxloc
order by personid, classid;
Here's one approach you could use:
SELECT *
FROM
(
SELECT
*,
(SELECT COUNT(DISTINCT t2.LocationID) FROM tab t2 WHERE t2.PersonId = t1.PersonId) AS LocationCount
FROM tab t1
) t
WHERE
t.LocationCount = 1 --get all records for those at the same location
OR (t.LocationCount > 1 AND t.IsEnrolled = 1) --get only current records for those # multiple location
Essentially, you count the locations a person is enrolled at. If they are just at one, take all the records, and if multiple take only the current enrollment.
You can get the personids using an aggregation query:
select personid
from #test t
where isenrolled = 1
group by personid
having min(location) = max(location);
At that point, it is easy enough to use in to get everything else:
select t.*
from #test t
where personid in (select personid
from #test t
where isenrolled = 1
group by personid
having min(location) = max(location)
);
one join
select t2.PersonId, t2.ClassId, t2.IsEnrolled, t2.IsExited
from #Test t1
left join #Test t2
on t2.PersonId = t1.PersonId
and ((t2.IsEnrolled = 0 and t2.LocationId <> t1.LocationId)
or (t2.IsEnrolled = 1)
)
where t1.IsEnrolled = 1