Winrate of opponent vs another apponent - sql-server

I am trying to find the most efficient way to build a counter warehouse.
shortened the tables just to needed info for question
Match Table
MatchId, version, type
MatchParticipants
matchId, playerid, characterid, teamid (only team1 or team2), winner (1 or 0)
10 of these rows per match
Characters
characterid, name
So i have thought of doing a cross join of characters on characters and that gives me all the possibilities of opponents for said character then i would have do a massive subquery to look into the matches table where the id's were on opposite teams.
Any ideas essentially what i see the warehouse table looking like.
character1.characterid, character1.name,
character2.characterid, character2.name,
winrate of character1 over character2

I think that this query should meet your expectations:
SELECT x.[first] AS CharacterId,
c.name,
x.[second] AS CharacterId,
c1.name,
wins/loses AS ratio
FROM
(
SELECT m.CharacterId [first],
m1.CharacterId [second],
SUM(m.winner) AS wins,
SUM(m1.winner) AS loses
FROM MatchParticipants m
INNER JOIN MatchParticipants m1 ON m.MatchId = m1.MatchId AND m.TeamId <> m1.TeamId
GROUP BY
m.CharacterId,
m1.CharacterId
) x
LEFT JOIN Characters c ON x.[first] = c.characterid
LEFT JOIN Characters c1 ON x.[second] = c1.characterid

is this what you're looking for?
SELECT
*,
100.0 * matches/wins AS winRate
FROM
(SELECT
teamid,
playerid,
characterid AS opponentID,
COUNT(matchId) AS matches,
SUM(winner) AS wins
FROM
MatchParticipants
GROUP BY
teamid,
playerid,
characterid) w

Related

Interview question help on relatively basic JOIN and subqueries

I was asked to:
Print the following sequence of columns for each plant that only blooms in one type of weather.
WEATHER_TYPE
PLANT_NAME"
Schema
PLANTS (table name)
PLANT_NAME, string, The name of the plant. This is the primary key.
PLANT_SPECIES, sting, The species of the plant.
SEED_DATE, date, The date the seed was planted.
WEATHER (table name)
PLANT_SPECIES, string, The species of the plant.
WEATHER_TYPE, string, The type of weather in which the plant will bloom.
I wrote the script below and tested it against sample input and achieved a desired result. I don't know if this is what is considered a 'printed' result.
Seeking understanding on what I might have missed. How might I make this script 'more efficient' and/or 'better' and/or 'more robust'?
SELECT WEATHER.WEATHER_TYPE, a.PLANT_NAME
FROM (SELECT b.PLANT_NAME, b.PLANT_SPECIES
FROM (SELECT PLANTS.PLANT_NAME, PLANTS.PLANT_SPECIES, PLANTS.SEED_DATE, WEATHER.WEATHER_TYPE
FROM PLANTS JOIN WEATHER
ON PLANTS.PLANT_SPECIES = WEATHER.PLANT_SPECIES) b
GROUP BY b.PLANT_NAME, b.PLANT_SPECIES
HAVING count(*) = 1) a JOIN WEATHER
ON a.PLANT_SPECIES = WEATHER.PLANT_SPECIES
I achieved the expected result in a SQL Server Management Studio window, but not sure if it's the 'printed' result the question-askers are looking for.
I personally consider CTEs easier to read and to debug, compared to nested "Table Expressions", as you have done. I would have done something like:
with
x as (
select p.plant_name
from plants p
join weather w on w.plant_species = p.plant_species
group by p.plant_name
having count(*) = 1
)
select x.plant_name, w.weather_type
from x
join weather w on w.plant_species = x.plant_species
I have to agree with The Impaler in regards to the readability and ease of debugging nested table expressions. As another option to the CTE (which is really the better choice), if you really want to nest things without overthinking it you can use a correlated subquery. It's easier to read, though as your result set grows you'll lose efficiency.
SELECT w.weather_type, p.plant_name
FROM plants p
JOIN weather w
ON w.plant_species = p.plant_species
WHERE (SELECT COUNT(1) FROM dbo.weather WHERE plant_species = w.plant_species) = 1
or with grouping...
SELECT w.weather_type, p.plant_name
FROM plants p
JOIN weather w
ON w.plant_species = p.plant_species
WHERE w.plant_species IN (SELECT plant_species FROM dbo.weather GROUP BY plant_species HAVING COUNT(1) = 1)
SELECT w.weather_type, p.plant_name
FROM plants p
JOIN weather w
ON w.plant_species = p.plant_species
WHERE w.weather_type="Sunny";

Understand below SQL Query

I need your help to understand below query can anyone help me to describe it, i wanted to know the role of b.id is null and r.id is null in below query if anyone can explain whole code then it would be great?
select l.id as start,
(
select min(a.id) as id
from sequence as a
left outer join sequence as b on a.id = b.id - 1
where b.id is null
and a.id >= l.id
) as end
from sequence as l
left outer join sequence as r on r.id = l.id - 1
where r.id is null;
This query returns your "islands"of your sequence, i.e. start and end of continuos id intervals.
You can read more on gaps and islands here: Special Islands and here The SQL of Gaps and Islands in Sequences
The query is finding islands of consecutive numbers, outputting the start and end of ranges where all consecutive numbers are there, so for the set if numbers
{1,3,4,5,6,9,10}
I would expect
1,1
2,6
9,10
to be selected
The outer query starts by finding number (N) that cannot join to a record holding N-1, detected by r.id is null
The sub query then finds the next highest number (M) that does not join to a record holding M+1 (detected using b.id is null)
so in my example 3 does not have a '2' to join to, meaning 3 begins a range. The first number >= to that with no subsequent record is 6, which has no '7' to make a join to
The query is joining two tables (in this case the same table but it does not matter) using an outer join.
if we have
SELECT t1.a, t1.b, t2.d
FROM table1 t1
LEFT OUTER JOIN table2 t2 ON t1.a = t2.a
WHERE t2.a is null
This means it returns a set of records with all those from table1, however its possible there will not be a joined record from table2 for every record in table1. If there isn't the table2 fields are returned as null so the the WHERE clause is effectively saying return me all records from table1 where we do not have a join record in table2.
In your example where it joins onto itself you are looking where there is not a next/previous record (depending upon which way you are looking at it) based upon the id e.g. if id = 5, then there is not a record with id = 4
Overall the sql as a whole looks like its returning the consecutive id ranges in the sequence table.

Combine multiple left joins in 1 query

I have two queries that I would like to combine. One query is left joining columns in the same table, the other query is left joining columns from two different tables. Both queries have the same table, just unsure how to properly set up the query.
1st Query:
SELECT BIZ_GROUP,
ORDER_ID,
STATION,
A.TC_DATE,
WANT_DATE,
TIME_SLOT,
JOB_CODE,
[ADDRESS],
CITY,
A.TECH_ID,
A.PREMISE,
ISNULL(B.LAST_ARRIVED, A.LAST_ARRIVE) AS ARRIVED,
ORDER_CLOSED,
COMP_STATUS,
WORK_STATUS,
REMARKS,
CORRECTION
FROM MET_timecommit A
LEFT JOIN(SELECT premise,
TC_DATE,
TECH_ID,
MIN(last_arrive) AS LAST_ARRIVED
FROM MET_timecommit
WHERE PREMISE IS NOT NULL
GROUP BY premise,
TC_DATE,
TECH_ID) B ON B.TC_DATE = A.TC_DATE
AND B.PREMISE = A.PREMISE
2nd query:
SELECT *
FROM MET_timecommit
LEFT JOIN (SELECT ORDER_ID,
created,
host_creation,
went_to
FROM workload
WHERE went_to >= getdate()-365) C ON C.went_to=MET_timecommit.TC_DATE
AND C.order_id=MET_timecommit.order_id
Evidently I am not used to this forum. You all don't have to be so rude. TDP was able to help me out based on what I provided. All other comments were unnecessary.
This should bring back the rows for both tables B and C for each row of table A:
SELECT A.BIZ_GROUP,
A.ORDER_ID,
A.STATION,
A.TC_DATE,
A.WANT_DATE,
A.TIME_SLOT,
A.JOB_CODE,
A.[ADDRESS],
A.CITY,
A.TECH_ID,
A.PREMISE,
ISNULL(B.LAST_ARRIVED, A.LAST_ARRIVE) AS ARRIVED,
A.ORDER_CLOSED,
A.COMP_STATUS,
A.WORK_STATUS,
A.REMARKS,
A.CORRECTION,
C.*
FROM MET_timecommit A
LEFT JOIN(SELECT premise,
TC_DATE,
TECH_ID,
MIN(last_arrive) AS LAST_ARRIVED
FROM MET_timecommit
WHERE PREMISE IS NOT NULL
GROUP BY premise,
TC_DATE,
TECH_ID) B ON B.TC_DATE = A.TC_DATE
AND B.PREMISE = A.PREMISE
LEFT JOIN (SELECT ORDER_ID,
created,
host_creation,
went_to
FROM workload
WHERE went_to >= getdate()-365) C ON C.went_to=A.MET_timecommit.TC_DATE
AND C.order_id=A.MET_timecommit.order_id

SQL: Select a column independent of where clause

SELECT TOP 1000 p.Title,p.Distributor, SUM(r.SalesVolume) AS VolumeOfSales,
CAST(SUM(r.CustomerPrice*r.SalesVolume) as decimal (18,0)) AS ValueOfSales,
CAST (AVG(r.CustomerPrice) as decimal (18,1)) AS AvgPrice,
p.MS_ContentType AS category ,Min(c.WeekId) AS ReleaseWeek
from Product p
INNER JOIN RawData r
ON p.ProductId = r.ProductId
INNER JOIN Calendar c
ON r.DayId = c.DayId
WHERE c.WeekId BETWEEN ('20145231') AND ('20145252')
AND p.Distributor IN ('WARNER', 'TF1', 'GAUMONT')
AND p.VODEST IN ('VOD', 'EST')
AND p.ContentFlavor IN ('SD', 'HD', 'NC')
AND p.MS_ExternalID1 IN ('ADVENTURE/ACTION', 'ANIMATION/FAMILY', 'COMEDY')
AND p.MS_ContentType IN ('FILM', 'TV', 'OTHERS')
AND r.CountryId = 1
GROUP BY p.Title,p.Distributor,p.MS_ContentType
ORDER BY VolumeOfSales DESC, ValueOfSales DESC
I want to madify the above query so that only the column ReleaseWeek is independent of the where clause WHERE c.WeekId BETWEEN ('20145231') AND ('20145252')
The result that I dervive looks like:
`Title Distributor VolumeOfSales ValueOfSales AvgPrice category ReleaseWeek
Divergente M6SND 94038 450095 4.0 Film 20145233`
However what I really want is the ReleaseWeek to be the first value in the column c.WeekId corresponding to that Titlein the database and not the first one between ('20145231') AND ('20145252') What is the best way to modify it? Any leads would be greatful.

How to SELECT DISTINCT Info with TOP 1 Info and an Order By FROM the Top 1 Info

I have 2 tables, that look like:
CustomerInfo(CustomterID, CustomerName)
CustomerReviews(ReviewID, CustomerID, Review, Score)
I want to search reviews for a string and return CustomerInfo.CustomerID and CustomerInfo.CustomerName. However, I only want to show distinct CustomerID and CustomerName along with just one of their CustomerReviews.Reviews and CustomerReviews.Score. I also want to order by the CustomerReviews.Score.
I can't figure out how to do this, since a customer can leave multiple reviews, but I only want a list of customers with their highest scored review.
Any ideas?
This is the greatest-n-per-group problem that has come up dozens of times on Stack Overflow.
Here's a solution that works with a window function:
WITH CustomerCTE (
SELECT i.*, r.*, ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY Score DESC) AS RN
FROM CustomerInfo i
INNER JOIN CustomerReviews r ON i.CustomerID = r.CustomerID
WHERE CONTAINS(r.Review, '"search"')
)
SELECT * FROM CustomerCTE WHERE RN = 1
ORDER BY Score;
And here's a solution that works more broadly with RDBMS brands that don't support window functions:
SELECT i.*, r1.*
FROM CustomerInfo i
INNER JOIN CustomerReviews r1 ON i.CustomerID = r1.CustomerID
AND CONTAINS(r1.Review, '"search"')
LEFT OUTER JOIN CustomerReviews r2 ON i.CustomerID = r2.CustomerID
AND CONTAINS(r1.Review, '"search"')
AND (r1.Score < r2.Score OR r1.Score = r2.Score AND r1.ReviewID < r2.ReviewID)
WHERE r2.CustomerID IS NULL
ORDER BY Score;
I'm showing the CONTAINS() function because you should be using the fulltext search facility in SQL Server, not using LIKE with wildcards.
I voted for Bill Karwin's answer, but I thought I'd throw out another option.
It uses a correlated subquery, which can often incur performance problems with large data sets, so use with caution. I think the only upside is that the query is easier to immediately understand.
select *
from [CustomerReviews] r
where [ReviewID] =
(
select top 1 [ReviewID]
from [CustomerReviews] rInner
where rInner.CustomerID = r.CustomerID
order by Score desc
)
order by Score desc
I didn't add the string search filter, but that can be easily added.
I think this should do it
select ci.CustomterID, ci.CustomerName, cr.Review, cr.Score
from CustomerInfo ci inner join
(select top 1*
from CustomerReviews
where Review like '%search%'
order by Score desc) cr on ci.CustomterID = cr.CustomterID
order by cr.Score

Resources