If I have such data:
Id, Team1, Team2, Score1, Score2
--------------------------------
1, Aaa, Bbb, 10, 8
2, Aaa, Bbb, 6, 8
3, Aaa, Bbb, 6, 5
4, Bbb, Aaa, 9, 7
5, Aaa, Ccc, 12, 6
How I can get a column with difference between scores among teams (running aggregate)?
Example:
Id, Team1, Team2, Score1, Score2 Abs(DIFF)
-------------------------------- ---------
1, Aaa, Bbb, 10, 8 2
2, Aaa, Bbb, 6, 8 0
3, Aaa, Bbb, 6, 5 1
4, Bbb, Aaa, 7, 9 3
5, Aaa, Ccc, 12, 6 6
Please notice two things in the above example:
Order of teams among pair may vary (like in between row 1 and 4)
Table may contain many team pairs
Is it possible to get this DIFF column without using cursor or an update trick?
Query has to run on SQL Server 2008.
I rarely do DB work so simple explanations are welcome.
UPDATE Additional explanation:
In the first row the DIFF is 2 because team Aaa won by two points
In the second row the DIFF is 0 because this time team Bbb won by two points
In the third row the DIFF is 1 because team Aaa won again (by one point)
In the forth row the DIFF is 3 because team Aaa won again (by two points)
So by looking at the DIFF value, one should be able to answer a question like "what is the current difference in score between teams?".
Going with your second sample data (row 3 is tied in first sample, and row 4's score switch sides):
declare #t table (Id int not null,Team1 char(3) not null,Team2 char(3) not null,
Score1 int not null,Score2 int not null)
insert into #t(Id, Team1, Team2, Score1, Score2) values
(1, 'Aaa', 'Bbb', 10, 8),
(2, 'Aaa', 'Bbb', 6, 8),
(3, 'Aaa', 'Bbb', 6, 5),
(4, 'Bbb', 'Aaa', 7, 9),
(5, 'Aaa', 'Ccc', 12, 6)
;With NormTeams as (
select Id,
CASE WHEN Team1 < Team2 THEN Team1 ELSE Team2 END as Team1,
CASE WHEN Team1 < Team2 THEN Team2 ELSE Team1 END as Team2,
CASE WHEN Team1 < Team2 THEN Score1 ELSE Score2 END as Score1,
CASE WHEN Team1 < Team2 THEN Score2 ELSE Score1 END as Score2,
(Score1 - Score2) * CASE WHEN Team1 < Team2 THEN 1 ELSE -1 END as ScoreDiff
from #t
), MatchingTeams as (
select *,ROW_NUMBER() OVER (PARTITION By Team1,Team2 ORDER BY ID) as rn
from NormTeams
)
select *,
(SELECT SUM(ScoreDiff) from MatchingTeams mt2 where
mt2.Team1 = mt1.Team1 and mt2.Team2 = mt1.Team2 and
mt2.rn <= mt1.rn) as RunningDiff
from MatchingTeams mt1
There's unfortunately nothing much that can make it neater until you can use the 2012 windowed aggregates.
Result:
Id Team1 Team2 Score1 Score2 ScoreDiff rn RunningDiff
----------- ----- ----- ----------- ----------- ----------- -------------------- -----------
1 Aaa Bbb 10 8 2 1 2
2 Aaa Bbb 6 8 -2 2 0
3 Aaa Bbb 6 5 1 3 1
4 Aaa Bbb 9 7 2 4 3
5 Aaa Ccc 12 6 6 1 6
You'll notice that the first thing I do is the obvious - switch teams and scores around, if necessary, so that the rest of the query can assume that they do appear in a consistent order.
try this:
;WITH CTE as
(
select id,Team1,Team2,Score1,Score2,0 as RS1,0 as RS2,ABS(Score1-Score2) as DIFF from Table1 where id=1
UNION ALL
select t1.id,t1.Team1,t1.Team2,t1.Score1,t1.Score2,c.score1+t1.Score1,c.score2+t1.Score2,ABS((c.score1+t1.Score1)-(c.score2+t1.Score2)) as DIFF
from Table1 t1 inner join CTE c
on c.id+1=t1.id and ((t1.Team1 = c.team1 and t1.Team2 = c.Team2) or (t1.Team2 = c.team1 and t1.Team1 = c.Team2))
)
,CTE1 As
(
select * from CTE
union
select id,Team1,Team2,Score1,Score2,0,0,ABS(Score1-Score2) as DIFF from Table1 where id not in(select id from CTE)
)
select id,Team1,Team2,Score1,Score2,DIFF from CTE1
You could try this:
Edit: Fixed for team1 & team2 swaps.
; WITH CTE AS (
SELECT
Id
, Team1
, Team2
, Score1
, Score2
, ROW_NUMBER() OVER (PARTITION BY X.Teams ORDER BY ???) AS Row
FROM MyTable
CROSS APPLY (
SELECT
Team1 + '_' + Team2 -- Use something else than '_' if this caracter can exist in team's names
WHERE Team1 < Team2
UNION ALL
SELECT
Team2 + '_' + Team1
WHERE Team1 > Team2
) AS X(Teams)
)
SELECT
T1.Id
, T1.Team1
, T1.Team2
, T1.Score1
, T1.Score2
, ABS((T1.Score1 + ISNULL(T2.Score1, 0)) - (T1.Score2 + ISNULL(T2.Score2, 0))) AS Abs(DIFF)
FROM CTE AS T1
OUTER APPLY (
SELECT
SUM(T2.Score1) AS Score1
, SUM(T2.Score2) AS Score2
FROM CTE AS T2
WHERE T2.Teams = T1.Teams
AND T2.Row < T1.Row
) AS T2
Related
The example below builds a table that extracts the first two score values by userId and passageId. How can I select only records where each record in the new table contains at least two scores (i.e. ignore records where score2 is null)?
Example
Code:
drop table if exists simon;
drop table if exists simon2;
Create table simon (userId int, passageId int, score int);
Create table simon2 (userId int, passageId int, score1 int,score2 int);
INSERT INTO simon (userId , passageId , score )
VALUES
(10, 1, 2),
(10, 1, 3),
(10, 2, 1),
(10, 2, 1),
(10, 2, 5),
(11, 1, 1),
(11, 2, 2),
(11, 2, 3),
(11, 3, 4);
insert into simon2(userId,passageId,score1,score2)
select t.userId, t.passageId,
substring_index(t.scores,',',1) as score1,
(case when length(t.scores) > 1 then substring_index(t.scores,',',-1)
else null
end
) as score2
from
(select userId,passageId,
substring_index (group_concat(score separator ','),',',2) as scores
from simon
group by userId,passageId) t;
select *from simon2;
This is what I get now:
userId passageId score1 score2
1 10 1 2 3
2 10 2 1 1
3 11 1 1 NULL
4 11 2 2 3
5 11 3 4 NULL
This is what I want:
userId passageId score1 score2
1 10 1 2 3
2 10 2 1 1
4 11 2 2 3
Just add this around your query
Select * from ( ...... ) x where score2 is not null
You have no ordering specifying what score goes into score_1 and score_2, so I'll just use min() and max(). You can then do the logic as:
select s.userid, s.passageid,
max(score) as score_1, max(score) as score_2
from simon s
group by s.userid, s.passageid
having count(*) >= 2;
This doesn't give you exactly the same results for 10/2. However, your results are arbitrary because the group_concat() has no order by. SQL tables represent unordered sets. There is no ordering unless you specify it.
If you want an ordering, then define the table as:
Create table simon (
simonId int auto_increment primary key,
userId int,
passageId int,
score int
);
And then use the simonId column appropriately.
I want to copy the third and the forth column but change the second column. What should I do? I want to know how to write the sql query. Thank you.
for example:
table1:
1, 1, aaa, bbb
2, 1, ads, bff
3, 1, awq, bcc
and I want table1 finally to be:
1, 1, aaa, bbb
2, 1, ads, bff
3, 1, awq, bcc
4, 2, aaa, bbb
5, 2, ads, bff
6, 2, awq, bcc
(the first column is id)
This should work:
insert into table1 (column2,column3,column4)
select 2,column3, column4
from table1 where column2 = 1
As you suggest:
select (#cnt:=#cnt + 1) as RowNumber,a.c1,a.c2,a.c3 from
(select 1 as id, 1 as c1, 'aaa' as c2, 'bbb'as c3 union all
select 2, 1, 'ads', 'bff' union all
select 3, 1, 'awq', 'bcc'
union all
select 1 as id, 1 as c1, 'aaa' as c2, 'bbb'as c3 union all
select 2, 1, 'ads', 'bff' union all
select 3, 1, 'awq', 'bcc') as a
cross JOIN
(select #cnt:=0) as tmp
RESULT:
1 1 aaa bbb
2 1 ads bff
3 1 awq bcc
4 1 aaa bbb
5 1 ads bff
6 1 awq bcc
I'm working on another SQL query.
I have the following table.
PURCHASES
ID CUST_ID PROD_CODE PURCH_DATE
1 1 'WER' 01/12/2012
2 2 'RRE' 02/10/2005
3 3 'RRY' 02/11/2011
4 3 'TTB' 15/05/2007
5 3 'GGD' 20/06/2016
6 2 'SSD' 02/10/2011
I'm trying to add another column PURCH_COUNT that would display the purchase count for the CUST_ID based on PURCH_DATE.
If this is a first purchase it would return 1, if second then 2, and so on.
So the result I'm hoping is:
ID CUST_ID PROD_CODE PURCH_DATE PURCH_COUNT
1 1 'WER' 01/12/2012 1
2 2 'RRE' 02/10/2005 1
3 3 'RRY' 02/11/2011 2
4 3 'TTB' 15/05/2007 1
5 3 'GGD' 20/06/2016 3
6 2 'SSD' 02/10/2011 2
Thanks in advance!
Sample Data
DECLARE #Table1 TABLE
(ID int, CUST_ID int, PROD_CODE varchar(7), PURCH_DATE datetime)
;
INSERT INTO #Table1
(ID, CUST_ID, PROD_CODE, PURCH_DATE)
VALUES
(1, 1, 'WER', '2012-01-12 05:30:00'),
(2, 2, 'RRE', '2005-02-10 05:30:00'),
(3, 3, 'RRY', '2011-02-11 05:30:00'),
(4, 3, 'TTB', '2008-03-05 05:30:00'),
(5, 3, 'GGD', '2017-08-06 05:30:00'),
(6, 2, 'SSD', '2011-02-10 05:30:00')
;
IN SQL :
select ID,
CUST_ID,
PROD_CODE,
PURCH_DATE,
ROW_NUMBER()OVER(PARTITION BY CUST_ID ORDER BY (SELECT NULL))RN
from #Table1
In MySql :
SELECT a.ID, a.CUST_ID,a.PROD_CODE,a.PURCH_DATE, (
SELECT count(*) from #Table1 b where a.CUST_ID >= b.CUST_ID AND a.ID = b.ID
) AS row_number FROM #Table1 a
Use a correlated sub-query to get the counts per customer.
SELECT t.*,
(SELECT 1+count(*)
FROM table1
WHERE t.cust_id = cust_id
AND t.purch_date > purch_date) as purch_cnt
FROM table1 t
ORDER BY cust_id,purch_date
SQL Fiddle
Any correlated subquery or window function can be expressed as a join, too. Sometimes a join is easier to understand, or is produced from components you can re-use, and sometimes the DBMS doesn't support the fancier feature. (AFAIK, a subquery in a SELECT clause is nonstandard.)
create table T
(ID, CUST_ID, PROD_CODE, PURCH_DATE);
INSERT INTO T
(ID, CUST_ID, PROD_CODE, PURCH_DATE)
VALUES
(1, 1, 'WER', '2012-01-12 05:30:00'),
(2, 2, 'RRE', '2005-02-10 05:30:00'),
(3, 3, 'RRY', '2011-02-11 05:30:00'),
(4, 3, 'TTB', '2008-03-05 05:30:00'),
(5, 3, 'GGD', '2017-08-06 05:30:00'),
(6, 2, 'SSD', '2011-02-10 05:30:00')
;
select PURCH_COUNT, T.*
from T join (
select count(b.ID) as PURCH_COUNT
, a.CUST_ID, a.PURCH_DATE
from T as a join T as b
on a.CUST_ID = b.CUST_ID
and b.PURCH_DATE <= a.PURCH_DATE
group by a.CUST_ID, a.PURCH_DATE
) as Q
on T.CUST_ID = Q.CUST_ID
and T.PURCH_DATE = Q.PURCH_DATE
;
Output of subquery:
PURCH_COUNT CUST_ID PURCH_DATE
----------- ---------- -------------------
1 1 2012-01-12 05:30:00
1 2 2005-02-10 05:30:00
2 2 2011-02-10 05:30:00
1 3 2008-03-05 05:30:00
2 3 2011-02-11 05:30:00
3 3 2017-08-06 05:30:00
Output of query:
PURCH_COUNT ID CUST_ID PROD_CODE PURCH_DATE
----------- ---------- ---------- ---------- -------------------
1 1 1 WER 2012-01-12 05:30:00
1 2 2 RRE 2005-02-10 05:30:00
2 3 3 RRY 2011-02-11 05:30:00
1 4 3 TTB 2008-03-05 05:30:00
3 5 3 GGD 2017-08-06 05:30:00
2 6 2 SSD 2011-02-10 05:30:00
I am trying to write a query in sql which gives me distinct Pan details and the count of that Pan in the table where the Name is appearing more than once
For example: Consider the table below
ID Name Pan
1 ABC 123
2 ABC 123
3 DEF 456
4 ABC 124
5 WW 234
6 WW 2345
The result expected is :
Pan Name Count1
123 ABC 2
124 ABC 1
234 WW 1
2345 WW 1
Could someone please help me out with this.
Try this. Hope below statement help you out.
SELECT PAN, NAME , COUNT(ID) FROM TAB_PAN GROUP BY PAN, NAME HAVING COUNT(ID)>1
As per conversation the requirement seems different.
The below code will work fine.
Schema for given data
SELECT * INTO #TAB FROM (
SELECT 1 ID , 'ABC' NAME , 123 PAN
UNION ALL
SELECT 2, 'ABC', 123
UNION ALL
SELECT 3 , 'DEF' ,456
UNION ALL
SELECT 4 , 'ABC', 124
UNION ALL
SELECT 5 , 'WW' , 234
UNION ALL
SELECT 6 , 'WW' ,2345
)AS A
Logic for the required case
SELECT PAN, NAME,COUNT(ID) ACTUAL_COUNT FROM #TAB t
WHERE (SELECT COUNt(NAME) FROM #TAB WHERE NAME= t.NAME) >1
GROUP BY PAN, NAME
SELECT DISTINCT x.*
FROM
( SELECT pan
, name
, COUNT(*) total
FROM my_table
GROUP
BY pan
, name
) x
JOIN my_table y
ON y.name = x.name
AND y.pan <> x.pan
I have a single table with a self reference InReplyTo with some data like this:
PostID InReplyTo Depth
------ --------- -----
1 null 0
2 1 1
3 1 1
4 2 2
5 3 2
6 4 3
7 1 1
8 5 3
9 2 2
I want to write a query that will return this data in it's threaded form so that the post with ID=2 and all it's descendants will output before PostID=3 and so on for unlimited depth
PostID InReplyTo Depth
------ --------- -----
1 null 0
2 1 1
4 2 2
6 4 3
9 2 2
3 1 1
5 3 2
8 5 3
7 1 1
Is there a simple way to achieve this? I am able to modify the DB structure at this stage so would the new hierarchy datatype be the easiest way to go? Or perhaps a recursive CTE?
-- Test table
declare #T table (PostID int, InReplyTo int, Depth int)
insert into #T values (1, null, 0), (2, 1, 1), (3, 1, 1), (4, 2, 2),
(5, 3, 2), (6, 4, 3), (7, 1, 1), (8, 5, 3),(9, 2, 2)
-- The post to get the hierarchy from
declare #PostID int = 1
-- Recursive cte that builds a string to use in order by
;with cte as
(
select T.PostID,
T.InReplyTo,
T.Depth,
right('0000000000'+cast(T.PostID as varchar(max)), 10)+'/' as Sort
from #T as T
where T.PostID = #PostID
union all
select T.PostID,
T.InReplyTo,
T.Depth,
C.Sort+right('0000000000'+cast(T.PostID as varchar(max)), 10)+'/'
from #T as T
inner join cte as C
on T.InReplyTo = C.PostID
)
select PostID,
InReplyTo,
Depth,
Sort
from cte
order by Sort
Result:
PostID InReplyTo Depth Sort
----------- ----------- ----------- --------------------------------------------
1 NULL 0 0000000001/
2 1 1 0000000001/0000000002/
4 2 2 0000000001/0000000002/0000000004/
6 4 3 0000000001/0000000002/0000000004/0000000006/
9 2 2 0000000001/0000000002/0000000009/
3 1 1 0000000001/0000000003/
5 3 2 0000000001/0000000003/0000000005/
8 5 3 0000000001/0000000003/0000000005/0000000008/
7 1 1 0000000001/0000000007/
What you are looking for is indeed a recursive query.
A matching example to your case can be found here