I want to know who has the most friends from the app I own(transactions), which means it can be either he got paid, or paid himself to many other users.
I can't make the query to show me only those who have the max friends number (it can be 1 or many, and it can be changed so I can't use limit).
;with relationships as
(
select
paid as 'auser',
Member_No as 'afriend'
from Payments$
union all
select
member_no as 'auser',
paid as 'afriend'
from Payments$
),
DistinctRelationships AS (
SELECT DISTINCT *
FROM relationships
)
select
afriend,
count(*) cnt
from DistinctRelationShips
GROUP BY
afriend
order by
count(*) desc
I just can't figure it out, I've tried count, max(count), where = max, nothing worked.
It's a two columns table - "Member_No" and "Paid" - member pays the money, and the paid is the one who got the money.
Member_No
Paid
14
18
17
1
12
20
12
11
20
8
6
3
2
4
9
20
8
10
5
20
14
16
5
2
12
1
14
10
It's from Excel, but I loaded it into sql-server.
It's just a sample, there are 1000 more rows
It seems like you are massively over-complicating this. There is no need for self-joining.
Just unpivot each row so you have both sides of the relationship, then group it up by one side and count distinct of the other side
SELECT
-- for just the first then SELECT TOP (1)
-- for all that tie for the top place use SELECT TOP (1) WITH TIES
v.Id,
Relationships = COUNT(DISTINCT v.Other),
TotalTransactions = COUNT(*)
FROM Payments$ p
CROSS APPLY (VALUES
(p.Member_No, p.Paid),
(p.Paid, p.Member_No)
) v(Id, Other)
GROUP BY
v.Id
ORDER BY
COUNT(DISTINCT v.Other) DESC;
db<>fiddle
Related
I have table that shows these information
Month NewClients OnHoldClients
5-2017 10 2
6-2017 16 4
7-2017 11 1
8-2017 15 6
9-2017 18 7
I am trying to find the accumulative total for each month
which is
(NewClients - OnHoldClients) + Previous Month Total
Something like this
Month NewClients OnHoldClients Total
5-2017 10 2 8
6-2017 16 4 20
7-2017 11 1 30
8-2017 15 6 39
9-2017 18 7 50
the query i tried to build was something like this but I think should be an easier way to do that
UPDATE MyTable
SET Total = (SELECT TOP 1 Total FROM MyTable B WHERE B.Month < A.Month) + NewClients - OnHoldClients
FROM MyTable A
Before we begin, note the mere fact that you're facing such calculative problem is a symptom that maybe you don't have the best possible design. Normally for this purpose calculated values are being stored along the way as the records are inserted. So i'd say you'd better have a total field to begin with and calculate it as records amass.
Now let's get down to the problem at hand. i composed a query which does that nicely but it's a bit verbose due to recursive nature of the problem. However, it yields the exact expected result:
DECLARE #dmin AS date = (SELECT min(mt.[Month]) from dbo.MyTable mt);
;WITH cte(_Month, _Total) AS (
SELECT mt.[Month] AS _Month, (mt.NewClients - mt.OnHoldClients) AS _Total
FROM dbo.MyTable mt
WHERE mt.[Month] = #dmin
UNION ALL
SELECT mt.[Month] AS _Month, ((mt.NewClients - mt.OnHoldClients) + ccc._Total) AS _Total
FROM dbo.MyTable mt
CROSS APPLY (SELECT cc._Total FROM (SELECT c._Total,
CAST((row_number() OVER (ORDER BY c._Month DESC)) AS int) as _Rank
FROM cte c WHERE c._Month < mt.[Month]) as cc
WHERE cc._Rank = 1) AS ccc
WHERE mt.[Month] > #dmin
)
SELECT c._Month, max(c._Total) AS Total
FROM cte c
GROUP BY c._Month
It is a recursive CTE structure that goes about each record all along the way to the initial month and adds up to the final Total value. This query only includes Month and Total fields but you can easily add the other 2 to the list of projection.
Try this
;WITH CTE([Month],NewClients,OnHoldClients)
AS
(
SELECT '5-2017',10,2 UNION ALL
SELECT '6-2017',16,4 UNION ALL
SELECT '7-2017',11,1 UNION ALL
SELECT '8-2017',15,6 UNION ALL
SELECT '9-2017',18,7
)
SELECT [Month],
NewClients,
OnHoldClients,
SUM(MonthTotal)OVER( ORDER BY [Month]) AS Total
FROM
(
SELECT [Month],
NewClients,
OnHoldClients,
SUM(NewClients-OnHoldClients)OVER(PArtition by [Month] Order by [Month]) AS MonthTotal
FROM CTE
)dt
Result,Demo:http://rextester.com/DKLG54359
Month NewClients OnHoldClients Total
--------------------------------------------
5-2017 10 2 8
6-2017 16 4 20
7-2017 11 1 30
8-2017 15 6 39
9-2017 18 7 50
This is the input table:
Customer_ID Date Amount
1 4/11/2014 20
1 4/13/2014 10
1 4/14/2014 30
1 4/18/2014 25
2 5/15/2014 15
2 6/21/2014 25
2 6/22/2014 35
2 6/23/2014 10
There is information pertaining to multiple customers and I want to get a rolling sum across a 3 day window for each customer.
The solution should be as below:
Customer_ID Date Amount Rolling_3_Day_Sum
1 4/11/2014 20 20
1 4/13/2014 10 30
1 4/14/2014 30 40
1 4/18/2014 25 25
2 5/15/2014 15 15
2 6/21/2014 25 25
2 6/22/2014 35 60
2 6/23/2014 10 70
The biggest issue is that I don't have transactions for each day because of which the partition by row number doesn't work.
The closest example I found on SO was:
SQL Query for 7 Day Rolling Average in SQL Server
but even in that case there were transactions made everyday which accomodated the rownumber() based solutions
The rownumber query is as follows:
select customer_id, Date, Amount,
Rolling_3_day_sum = CASE WHEN ROW_NUMBER() OVER (partition by customer_id ORDER BY Date) > 2
THEN SUM(Amount) OVER (partition by customer_id ORDER BY Date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
END
from #tmp_taml9
order by customer_id
I was wondering if there is way to replace "BETWEEN 2 PRECEDING AND CURRENT ROW" by "BETWEEN [DATE - 2] and [DATE]"
One option would be to use a calendar table (or something similar) to get the complete range of dates and left join your table with that and use the row_number based solution.
Another option that might work (not sure about performance) would be to use an apply query like this:
select customer_id, Date, Amount, coalesce(Rolling_3_day_sum, Amount) Rolling_3_day_sum
from #tmp_taml9 t1
cross apply (
select sum(amount) Rolling_3_day_sum
from #tmp_taml9
where Customer_ID = t1.Customer_ID
and datediff(day, date, t1.date) <= 3
and t1.Date >= date
) o
order by customer_id;
I suspect performance might not be great though.
I have this table ScoreDetails, 2 columns (there are more, but only 2 needed or this query). One is ScoreDate, Score.
The structure is like
2012:03:27: 5:06:37:134 27
2012:03:27: 5:06:37:276 37
2012:03:28: 4:12:97:019 19
2012:03:29: 7:06:37:134 7
2012:03:29: 8:06:37:134 0
2012:04:03: 12:06:37:739 16
2012:04:04: 23:21:15:834 33
2012:04:04: 15:08:24:697 12
2012:04:06: 5:06:37:134 0
2012:04:09: 5:06:37:134 2
2012:04:13: 5:06:37:134 92
What I want is to write a select query, without using temp table or cursor. Such that, I have a column that starts from 1 and keeps on increasing as 2,3 and so on, upto when the score is non-zero. But as soon as a zero is encountered in score column, it resets to 1 and then start again. Like this...
2012:03:27: 5:06:37:134 27 1
2012:03:27: 5:06:37:276 37 2
2012:03:28: 4:12:97:019 19 3
2012:03:29: 7:06:37:134 7 4
2012:03:29: 8:06:37:134 0 0
2012:04:03: 12:06:37:739 16 1
2012:04:04: 23:21:15:834 33 2
2012:04:04: 15:08:24:697 12 3
2012:04:06: 5:06:37:134 0 0
2012:04:09: 5:06:37:134 2 1
2012:04:13: 5:06:37:134 92 2
I am using SQL Server 2008 R2.
You can use common table expressions for that. I defined 2 anchor queries: one for records with 0 score and the other for the first record. Then you build up the result based on previous results until you find 0 score.
with cte
as
(
select ScoreDate, Score, ScoreRank, 0 as Value
from (select ScoreDate, Score, dense_rank() over (order by ScoreDate) ScoreRank
from ScoreDetails) X
where Score = 0
union all
select ScoreDate, Score, ScoreRank, 1 as Value
from (select ScoreDate, Score, dense_rank() over (order by ScoreDate) ScoreRank
from ScoreDetails) X
where Score <> 0 and ScoreRank = 1
union all
select X.ScoreDate, X.Score, X.ScoreRank, cte.Value + 1 as Value
from (select ScoreDate, Score, dense_rank() over (order by ScoreDate) ScoreRank
from ScoreDetails) X
inner join cte
on X.ScoreRank = cte.ScoreRank + 1
and X.Score <> 0
)
select ScoreDate, Score, Value, ScoreRank
from cte
order by ScoreDate
SQL Fiddle Demo
I won't spoil the fun of finding the solution yourself, but I will give you some hints on how to split the problem into smaller pieces:
Find all the records where the score is reset. Let's call this subquery the resetRecords.
Join the records of the original table to the resetRecords, such that every record has "its" reset record (i.e., the reset record that provides the base for its count).
Use ROW_NUMBER() OVER (PARTITION BY ... ) to assign the numbers.
Try to do this one step at a time. Beware: It won't be a simple query, so a solution with temp tables or cursors might be easier to understand and maintain.
Try something like this:
with x as (
select *, sum(case when Score=0 then 1 else 0 end) over(order by ScoreDate) as grp
from ScoreDetails
)
select ScoreDate, Score, row_number() over (partition by grp order by ScoreDate)
from x
order by ScoreDate
(as soon as a zero is encountered in score column, it resets to 1 and then start again, you said)
How do I find the unique groups that are present in my table, and display how often that type of group is used?
For example (SQL Server 2008R2)
So, I would like to find out how many times the combination of
PMI 100
RT 100
VT 100
is present in my table and for how many itemid's it is used;
These three form a group because together they are assigned to a single itemid. The same combination is assigned to id 2527 and 2529, so therefore this group is used at least twice. (usagecount = 2)
(and I want to know that for all types of groups that are appearing)
The entire dataset is quite large, about 5.000.000 records, so I'd like to avoid using a cursor.
The number of code/pct combinations per itemid varies between 1 and 6.
The values in the "code" field are not known up front, there are more than a dozen values on average
I tried using pivot, but I got stuck eventually and I also tried various combinations of GROUP-BY and counts.
Any bright ideas?
Example output:
code pct groupid usagecount
PMI 100 1 234
RT 100 1 234
VT 100 1 234
CD 5 2 567
PMI 100 2 567
VT 100 2 567
PMI 100 3 123
PT 100 3 123
VT 100 3 123
RT 100 4 39
VT 100 4 39
etc
Just using a simple group:
SELECT
code
, pct
, COUNT(*)
FROM myTable
GROUP BY
code
, pct
Not too sure if that's more like what you're looking for:
select
uniqueGrp
, count(*)
from (
select distinct
itemid
from myTable
) as I
cross apply (
select
cast(code as varchar(max)) + cast(pct as varchar(max)) + '_'
from myTable
where myTable.itemid = I.itemid
order by code, pct
for xml path('')
) as x(uniqueGrp)
group by uniqueGrp
Either of these should return each combination of code and percentage with a group id for the code and the total number of instances of the code against it. You can use them for also adding the number of instances of the specific code/pct combo too for determining % contribution etc
select
distinct
t.code, t.pct, v.groupcol, v.vol
from
[tablename] t
inner join (select code, rank() over(order by count(*)) as groupcol,
count(*) as vol from [tablename] s
group by code) v on v.code=t.code
or
select
t.code, t.pct, v.groupcol, v.vol
from
(select code, pct from [tablename] group by code, pct) t
inner join (select code, rank() over(order by count(*)) as groupcol,
count(*) as vol from [tablename] s
group by code) v on v.code=t.code
Grouping by Code, and Pct should be enough I think. See the following :
select code,pct,count(p.*)
from [table] as p
group by code,pct
I have a need to SELECT all the rows from a table where the selected rows are greater than the datetime of the previously selected row by a given constant number of minutes. An example probably speaks best.
The following represents the table of data - we will call it myTable.
guid fkGuid myDate
------- ------- ---------------------
1 100 2013-01-10 11:00:00.0
2 100 2013-01-10 11:05:00.0
3 100 2013-01-10 11:10:00.0
4 100 2013-01-10 11:15:00.0
5 100 2013-01-10 11:20:00.0
6 100 2013-01-10 11:25:00.0
7 100 2013-01-10 11:30:00.0
8 100 2013-01-10 11:35:00.0
9 100 2013-01-10 11:40:00.0
10 100 2013-01-10 11:50:00.0
11 100 2013-01-10 11:55:00.0
What I want to do is provide a constant increment (say 10 minutes) and get back all the rows from the first that are 10 minutes or more from the previous row. So, with 10 minutes the result set should look like this:
guid myDate
------- ---------------------
1 2013-01-10 11:00:00.0
3 2013-01-10 11:10:00.0
5 2013-01-10 11:20:00.0
7 2013-01-10 11:30:00.0
9 2013-01-10 11:40:00.0
11 2013-01-10 11:55:00.0
The constant is passed in as a variable so it could be anything. Let's say it was 23 minutes, then the result set should look like this:
guid myDate
------- ---------------------
1 2013-01-10 11:00:00.0
6 2013-01-10 11:25:00.0
10 2013-01-10 11:50:00.0
The last example shows that I start at row 0's time (11:00:00) add 23 minutes and get the next >= row which is 11:25:00, add 23 minutes to the new row's time and then get the next (11:50:00) and so on.
I have tried doing this with a CTE but although I can quite easily get back all my times or none of them, I can't seem to figure how to get the rows I need. My current test code using 23 minutes hard coded into the WHERE clause:
WITH myCTE AS
(
SELECT guid,
myDate,
ROW_NUMBER() OVER (PARTITION BY guid ORDER BY myDate ASC) AS rowNum
FROM myTable
WHERE fkGuid = 100
)
SELECT currentRow.guid, currentRow.myDate
FROM myCTE AS currentRow
LEFT OUTER JOIN
myCTE AS previousRow
ON currentRow.guid = previousRow.guid
AND currentRow.rowNum = previousRow.rowNum + 1
WHERE
currentRow.myDate > DATEADD(minute, 23, previousRow.myDate)
ORDER BY
currentRow.myDate ASC
This returns nothing. If I omit the WHERE clause I get all rows back (obviously because I'm not filtering).
What am I missing?
Any and all help would be very much appreciated as it always is!
#gilly3, hardly SQL voodoo
WITH CTE
AS
(
SELECT TOP 1
guid
,fkGuid
,myDate
,ROW_NUMBER() OVER (ORDER BY myDate) RowNum
FROM MyTable
UNION ALL
SELECT mt.guid
,mt.fkGuid
,mt.myDate
,ROW_NUMBER() OVER (ORDER BY mt.myDate)
FROM MyTable mt
INNER JOIN
CTE ON mt.myDate>=DATEADD(minute,23,CTE.myDate)
WHERE RowNum=1
)
SELECT guid
,fkGuid
,myDate
FROM CTE
WHERE RowNum=1
The SQL Fiddle is here
First, your join will never return any rows, regardless of the where clause. Guid and rowNum are both unique keys per row, so if the guid is the same, so will be the rowNum. You can see that the join always fails by adding a field from previousRow to your select list and running your query without the where clause.
Next, joining on rowNum + 1 prevents skipping rows. You will only select adjacent rows that satisfy the date filter.
There may be some SQL voodoo with recursive queries that will make this work, but there will be a huge performance hit. Filter the data in your application code. Eg, in C#:
List<DataRow> FilterByInterval(IEnumerable<DataRow> rows, string dateColumn, int minutes)
{
List<DataRow> filteredRows = new List<DataRow>();
DateTime lastDate = DateTime.MinValue;
foreach (DataRow row in rows)
{
DateTime dt = row.Field<DateTime>(dateColumn);
TimeSpan diff = dt - lastDate;
if (diff.TotalMinutes >= minutes)
{
filteredRows.Add(row);
lastDate = dt;
}
}
return rows;
}