How to select entries that are back to back? - database

How would I select c_user_id who have made back to back entries in a SQL Server 2008 database ?
Preferably people who have made more than 3 back to back entries like pras.chla#gmail.com below (sorting by c_id desc and c_id is an identity column)
c_id c_user_id c_entry
1427 xermadr.asdf#me.com 155575
1426 pras.chla#gmail.com 155829
1425 pras.chla#gmail.com 155826
1424 pras.chla#gmail.com 155828
1423 pras.chla#gmail.com 155830
1422 sdfe.qqol#gmail.com 155559
thanks again ?

One way
SELECT DISTINCT c_user_id
FROM tab t1
CROSS APPLY (SELECT 1 AS C
FROM (SELECT TOP 2 *
FROM tab t2
WHERE t2.c_id < t1.c_id
ORDER BY t2.c_id DESC) T
HAVING COUNT(c_user_id) = 2 AND COUNT(DISTINCT c_user_id) = 1 AND MIN(c_user_id) = t1.c_user_id) CA
Or another
WITH T AS
(
SELECT *,
ROW_NUMBER() OVER (order by c_id) -
ROW_NUMBER() OVER (PARTITION BY c_user_id order by c_id) AS Grp
FROM tab t1
)
SELECT DISTINCT c_user_id
FROM T
GROUP BY c_user_id, Grp
HAVING COUNT(*) >=3

;WITH someUserTableWithOrderNumber as
(
SELECT ROW_NUMBER ( ) OVER (order by c_id) OrderNumber,
c_id,
c_user_id,
c_entry
FROM someUserTable
)
SELECT DISTINCT a.c_user_id
FROM someUserTableWithOrderNumber a
JOIN someUserTableWithOrderNumber b on a.OrderNumber = b.OrderNumber + 1 AND a.c_user_id = b.c_user_id
JOIN someUserTableWithOrderNumber c on b.OrderNumber = c.OrderNumber + 1 AND b.c_user_id = c.c_user_id
JOIN someUserTableWithOrderNumber d on c.OrderNumber = d.OrderNumber + 1 AND c.c_user_id = d.c_user_id

Related

SQL Simple Join with two tables, but one is random

I am stuck with this. I have a simple set-up with two tables. One table is holding emailaddresses one table is holding vouchercodes. I want to join them in a third table, so that each emailaddress has one random vouchercode.
Unfortunatly I am stuck with this as there are no identic Ids to match both values. What I have so far brings no result:
Select
A.Email
B.CouponCode
FROM Emailaddresses as A
JOIN CouponCodes as B
on A.Email = B.CouponCode
A hint would be great as search did not bring me any further yet.
Edit -
Table A (Addresses)
-------------------
Column A | Column B
-------------------------
email1#gmail.com True
email2#gmail.com
email3#gmail.com True
email4#gmail.com
Table B (Voucher)
-------------------
ABCD1234
ABCD5678
ABCD9876
ABCD5432
Table C
-------------------------
column A | column B
-------------------------
email1#gmail.com ABCD1234
email2#gmail.com ABCD5678
email3#gmail.com ABCD9876
email4#gmail.com ABCD5432
Sample Data:
While joining without proper keys is not a good solution, for your case you can try this. (note: not tested, just a quick suggestion)
;with cte_email as (
select row_number() over (order by Email) as rownum, Email
from Emailaddresses
)
;with cte_coupon as (
select row_number() over (order by CouponCode) as rownum, CouponCode
from CouponCodes
)
select a.Email,b.CouponCode
from cte_email a
join cte_coupon b
on a.rownum = b.rownum
You want to randomly join records, one email with one coupon each. So create random row numbers and join on these:
select
e.email,
c.couponcode
from (select t.*, row_number() over (order by newid()) as rn from emailaddresses t) e
join (select t.*, row_number() over (order by newid()) as rn from CouponCodes t) c
on c.rn = e.rn;
Give a row number for both the tables and join it with row number.
Query
;with cte as(
select [rn] = row_number() over(
order by [Column_A]
), *
from [Table_A]
),
cte2 as(
select [rn] = row_number() over(
order by [Column_A]
), *
from [Table_B]
)
select t1.[Column_A] as [Email_Id], t2.[Column_A] as [Coupon]
from cte t1
join cte2 t2
on t1.rn = t2.rn;
Find a demo here

Update two columns by another column of next row from same table in sql

Here is my table
And my output should be
I want to update the closed_date,time for new_class_desc ='FLM' with next Update_date,Update_time but if new_class_desc is 'FollowupComments' then ignore it and update the next date as Closed_date
I was trying query somewhat like this..
;WITH cte as(
SELECT *
,row_number() OVER(ORDER BY Update_date,Update_time) rn
FROM Table
WHERE Problem_sid = 1435819
)
UPDATE c1 SET Closed_date = c2.Update_date, Closed_time = c2.Update_time
FROM cte c1
JOIN cte c2 ON c1.rn = c2.rn - 1
AND c1.New_class_desc = 'FLM'
AND c2.New_class_desc <> 'FLM'
AND c2.New_class_desc not in ('FollowUpComments')
But in this I am not getting new_class_desc =Bank update_date as Closed_date for Flm.
Please guide here.
WITH cte
AS
(
SELECT *, ROW_NUMBER() OVER(ORDER BY t.Update_date, t.Update_time) AS rno
FROM my_Table t
WHERE t.New_class_desc <> 'FollowUpComments'
)
UPDATE t
SET t.Closed_Date = t1.Closed_Date,
t.Closed_Time = t1.Closed_Time
FROM cte t
JOIN cte t1 ON t.rno = t1.rno + 1
WHERE t.New_class_desc = 'FLM'

Assign Tracking Number and Flag it

I have 2 tables
Table 1
Customer Name , Order id , Tracking Id
sailesh , 23546 , NULL
nilesh , 26362 , NULL
Table 2
Tracking id , Used
RN167367 , No
RN47282 , NO
I want assign a tracking number in table 1 for each order and then mark it as used.
Thanks in advance
First assign tracking numbers and then mark them:
;with cte1 as(select *, row_number() over(order by orderid) rn
from table1 where trackingid is null),
cte2 as(select *, row_number() over(order by trackingid) rn
from table2 where Used = 'NO')
update c1 set trackingid = c2.trackingid
from cte1 c1
join cte2 c2 on c1.rn = c2.rn
update t2 set Used = 'YES'
from table1 t1
join table2 t2 on t1.trackingid = t2.trackingid

MSSql only group those with count greater than 3 and return the rest records

I want to group the key with count greater than 3, and the query will return the rest of the records also. I don't want to use Union All, is there any other way to do it?
ID
1
1
1
2
3
3
4
4
4
4
Return
1
1
1
2
3
3
4
You can use ranking- and aggregate functions:
WITH CTE AS
(
SELECT ID,
CNT = COUNT(*) OVER (PARTITION BY ID),
RN = ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID)
FROM dbo.TableName
)
SELECT ID
FROM CTE
WHERE CNT <= 3 OR RN = 1
Demo
I'd do it like this
SELECT
GroupedData.ID
FROM
(SELECT ID, CNT = COUNT(*)
FROM dbo.TableName
GROUP BY ID) GroupedData AS g
LEFT JOIN dbo.TableName AS t
ON t.id = g.id and g.CNT<=3
This also allows you to add further columns which report details for the group or individual record as appropriate
SELECT
g.ID,
ISNULL(t.RecordName,'Grouped Records') as RecordName,
ISNULL(t.NumericField,g.NumericField) as NumericField
FROM
(
SELECT ID, CNT = COUNT(*), SUM(NumericField) as NumericField
FROM dbo.TableName
GROUP BY ID
) GroupedData AS g
LEFT JOIN dbo.TableName AS t
ON t.id = g.id and g.CNT<=3

How to use CROSS APPLY in this scenario

I have a ProductStatus table as listed below. I need to list all products whose latest status is “SU”. Along with that I need to list what was the previous status of this product.
Based on referring various posts, it seems like CROSS APPLY will be suitable for this. I made an attempt as listed below but that didn’t give the expected result.
What is the best way to achieve this in SQL Server 2005 (without using subquery)?
DECLARE #ProductStatus TABLE (ProductStatusID INT, productCode VARCHAR(5), statusCode VARCHAR(2))
INSERT INTO #ProductStatus
SELECT 1,'10011','RE' --Recevied
UNION
SELECT 2,'10011','SU' --Suspended
UNION
SELECT 3,'10012','IT' -- In Transit
UNION
SELECT 4,'10012','RE' -- Received
UNION
SELECT 10,'10012','PR' -- Produced
UNION
SELECT 12,'10012','SU' -- Suspended
UNION
SELECT 14,'10013','RE' -- Recevied
UNION
SELECT 16,'10014','SU' -- Recevied
UNION
SELECT 18,'10014','RE' -- Recevied
CROSS APPLY attempt
SELECT *
FROM #ProductStatus P
CROSS APPLY
(
SELECT MAX(V.ProductStatusID) as maxVal
FROM #ProductStatus V
WHERE V.ProductCode = P.ProductCode
AND V. ProductStatusID < P.ProductStatusID
GROUP BY V.ProductCode
)ML
WHERE P.statusCode = 'SU'
EXPECTED RESULT
You can do this with cross apply but I think row_number() is an easier approach:
select ProductCode,
max(case when seqnum = 1 then statusCode end) as LastStatus,
max(case when seqnum = 2 then statusCode end) as PrevStatus
from (select p.*,
row_number() over (partition by ProductCode order by ProductStatusId desc) as seqnum
from #ProductStatus p
) p
group by ProductCode
having max(case when seqnum = 1 then statusCode end) = 'SU';
Lijo, I've structured it as a CTE so you can see how I've developed my ideas. You can refactor it as sub-queries without affecting the meaning if you are more comfortable with those.
;with MostRecentStatus as (
select
MAX(ProductStatusID) as ProductStatusID,
productCode
from #ProductStatus as p1
group by productCode
)
,MostRecentIsSU as (
select
p2.ProductStatusID,
p2.productCode,
p2.statusCode
from MostRecentStatus as mrs
inner join #ProductStatus as p2
on p2.ProductStatusID = mrs.ProductStatusID
and p2.statusCode = 'SU'
)
select
m.ProductStatusID,
m.productCode,
m.statusCode,
p3.statusCode as PrevStatus,
p3.ProductStatusID as PrevProductStatusID
from MostRecentIsSU as m
left outer join #ProductStatus as p3
on p3.productCode = m.productcode
and p3.ProductStatusID = m.ProductStatusID - 1;
Edit: ..and here's the ROW_NUMBER() version with kudos to #attila.
;with InSquence as
(
select
ProductStatusID,
productCode,
statusCode,
ROW_NUMBER() OVER(PARTITION BY productCode ORDER BY ProductStatusID desc) as Sequence
from #ProductStatus
)
,FirstIsSU as
(
select
ProductStatusID,
productCode
from InSquence
where Sequence = 1
and statusCode = 'SU'
)
,PreviousCode as
(
select
ProductStatusID,
productCode,
statusCode
from InSquence
where Sequence = 2
)
select
f.ProductStatusID,
f.productCode,
'SU' as CurrentStatus,
p.statusCode as PrevStatus,
p.ProductStatusID as PrevProductStatusID
from FirstIsSU as f
left outer join PreviousCode as p
on p.productCode = f.ProductCode;
Here is a convoluted solution which serves mainly to illustrate that this should probably be done using row_number() :)
SELECT
F.productCode, F.statusCode, F.productStatusID, F.PriorProductStatusID, PriorStatus.statusCode
FROM
(
SELECT
PCS.productCode, PCS.statusCode, PCS.productStatusID, MAX(PS.productStatusID) PriorProductStatusID
FROM
(
SELECT productCode, MAX(productStatusID) productStatusID
FROM #ProductStatus
GROUP BY productCode
) LatestStatus
INNER JOIN
#ProductStatus PCS
ON PCS.productCode = LatestStatus.productCode
AND PCS.productStatusID = LatestStatus.productStatusID
AND PCS.statusCode = 'SU'
LEFT OUTER JOIN
#ProductStatus PS
ON PS.productCode = PCS.productCode
AND PS.productStatusID < PCS.productStatusID
GROUP BY PCS.productCode, PCS.statusCode, PCS.productStatusID
) F
LEFT OUTER JOIN
#ProductStatus PriorStatus
ON F.productCode = PriorStatus.productCode
AND F.PriorProductStatusID = PriorStatus.ProductStatusID

Resources