I've a data base with two tables.
Table Players Table Wins
ID Name ID Player_won
1 Mick 1 2
2 Frank 2 1
3 Sarah 3 4
4 Eva 4 5
5 Joe 5 1
I need a SQL query which show "The players who have not won any game".
I tried but I don't know even how to begin.
Thank you
You need all the rows from players that don't have corresponding rows in wins. For this you need a left join, filtering for rows that don't join:
select
p.id,
p.name
from Players p
left join Wins w on w.Player_won = p.id
where w.Player_won is null
You can also use not in:
select
id,
name
from Players
where id not in (select Player_won from Wins)
How about the MINUS set operator?
Sample data:
SQL> with players (id, name) as
2 (select 1, 'Mick' from dual union all
3 select 2, 'Ffrank' from dual union all
4 select 3, 'Sarah' from dual union all
5 select 4, 'Eva' from dual union all
6 select 5, 'Joe' from dual
7 ),
8 wins (id, player_won) as
9 (select 1, 2 from dual union all
10 select 2, 1 from dual union all
11 select 3, 4 from dual union all
12 select 4, 5 from dual union all
13 select 5, 1 from dual
14 )
Query begins here:
15 select id from players
16 minus
17 select player_won from wins;
ID
----------
3
SQL>
So, yes ... player 3 didn't win any game so far.
I think you should provide your attempts next time, but here you go:
select p.name
from players p
where not exists (select * from wins w where p.id = w.player_won);
MINUS is not the best option here because of not using indexes and instead performing a full-scan of both tables.
I've a data base with two tables.
You don't show the names or any definition of the tables, leaving me to make an educated guess about their structure.
I tried but I don't know even how to begin.
What exactly did you try? Possibly what you are missing here is the concept of a LEFT OUTER JOIN.
Assuming the tables are named player_table and wins_table, and have column names exactly as you showed, and that the player_won column is intended to express the number of games won by the player of that row's ID, and without knowing whether or not wins_table will have rows for players with zero wins… this should cover it:
select Name
from players_table pt
left join wins_table wt on (pt.ID = wt.ID)
-- Either this player is explicitly specified to have Player_won=0
-- or there is no row for this player ID in the wins table
-- (but excluding the possibility of an explicit NULL value, since its meaning would be unclear)
where Player_won = 0 or wt.ID is null;
As you can see from the variety of answers you've gotten, there are many ways to accomplish this.
One additional way to do this is to use COUNT in a correlated subquery, as in:
SELECT *
FROM PLAYERS p
WHERE 0 = (SELECT COUNT(*)
FROM WINS w
WHERE w.PLAYER_WON = p.ID)
db<>fiddle here
SELECT *
FROM Players p
INNER JOIN Wins w
ON p.ID = w.ID
WHERE w.players_won = 0
I have not done SQL in awhile but I think this might be right if you are looking for players with 0 wins
Related
How can I write a query that returns the sum of ages for a pair of people. I want to compare the current row with every other rows in the database except the previous ones? For example, I have a Person table and the table has 3 records (In my real table, I have a lot more). Person 1's age should be compared with Person 2 and Person 3 and Person 2 should be compared with Person 3. How can I accomplish this in query form?
You can generate a row with all the values using an inequality join:
select p1.*, p2.*
from person p1 join
person p2
on p1.id < p2.id;
What #gordon-linoff wrote as an answer for the join was correct. It's just missing the total calculation. Here is how the total can be accomplished (I had to open a new answer since I couldn't edit Gordon's answer, sorry):
SELECT
p1.id
, p1.name
, p2.id
, p2.name
, (p1.age + p2.age) TotalAge
FROM
person p1
JOIN person p2 ON p1.id < p2.id;
This compares [person.id =1 with person.id =2, 3, 4, ...], [person.id = 2 with person.id =3, 4, ...] and so on. The last p1.id will not be compared this way as there is no further p2.id greater (but that person will be included in all other comparisons as p2.id because it is larger than all others). This should satisfy the matching/joining requirement asked.
Whenever you want to do these kind of comparisons, its assumed that you have an order defined. In this case it appears to be person ID is the order.
You can use analytical function with a windowing clause to get the desired result.
You use SUM as the analytic function and consider the window of the rows between the next row all the way till the last row as per your order.
Thus the query becomes
WITH
persons
AS
(SELECT 1 AS personid, 'Person 1' AS name, 23 AS age FROM DUAL
UNION
SELECT 2 AS personid, 'Person 2' AS name, 34 AS age FROM DUAL
UNION
SELECT 3 AS personid, 'Person 3' AS name, 30 AS age FROM DUAL
UNION
SELECT 4 AS personid, 'Person 4' AS name, 28 AS age FROM DUAL)
SELECT personid,
name,
SUM (age)
OVER (ORDER BY personid ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING)
AS subsequent_sum_age
FROM persons;
The ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING is your windowing clause which tells Oracle which rows to consider for summation.
The result of this is
PERSONID NAME AGE SUBSEQUENT_SUM_AGE
1 Person 1 23 92
2 Person 2 34 58
3 Person 3 30 28
4 Person 4 28
EDIT: added third requirement after playing with solution from Tim Biegeleisen
EDIT2: modified Robbie's DOB to be before his parent's marriage date
I am trying to create a query that will look at two tables and determine the difference in dates based on a percentage. I know, super confusing... Let me try and explain using the tables below:
Bob and Mary are married on 2010-01-01 and expect 4 kids (Parent table)
I want to know how many years it took until they met 50% of their expected kids (i.e. 2/4 kids). Using the Child table to see the DOB of their 4 kids, we know that Frankie is the second child which meets our 50% threshold so we use Frankie's DOB and subtract it from Frankie's parent's marriage date and end up with 3 years!
If the goal isn't reached then display no value e.g. Mick and Jo only had 1 child so far so they haven't yet reached their goal
Hoping this is doable using BigQuery standard SQL.
Parent table
id married_couple married_at expected_kids
--------------------------------------
1 Bob and Mary 2010-01-01 4
2 Mick and Jo 2010-01-01 4
Child table
id child_name parent_id date_of_birth
--------------------------------------
1 Eddie 1 2012-01-01
2 Frankie 1 2013-01-01
3 Robbie 1 2005-01-01
4 Duncan 1 2015-01-01
5 Rick 2 2014-01-01
Expected SQL result
parent_id half_goal_reached(years)
--------------------------------------
1 3
2
Below both soluthions for BigQuery Standard SQL
First one is more in classic sql way, the second one is more of BigQuery style (I think)
First Solution: with analytics function
#standardSQL
SELECT
parent_id,
IF(
MAX(pos) = MAX(CAST(expected_kids / 2 AS INT64)),
MAX(DATE_DIFF(date_of_birth, married_at, YEAR)),
NULL
) AS half_goal_reached
FROM (
SELECT c.parent_id, c.date_of_birth, expected_kids, married_at,
ROW_NUMBER() OVER(PARTITION BY c.parent_id ORDER BY c.date_of_birth) AS pos
FROM `child` AS c
JOIN `parent` AS p
ON c.parent_id = p.id
)
WHERE pos <= CAST(expected_kids / 2 AS INT64)
GROUP BY parent_id
Second Solution: with use of ARRAY
#standardSQL
SELECT
parent_id,
DATE_DIFF(dates[SAFE_ORDINAL(CAST(expected_kids / 2 AS INT64))], married_at, YEAR) AS half_goal_reached
FROM (
SELECT
parent_id,
ARRAY_AGG(date_of_birth ORDER BY date_of_birth) AS dates,
MAX(expected_kids) AS expected_kids,
MAX(married_at) AS married_at
FROM `child` AS c
JOIN `parent` AS p
ON c.parent_id = p.id
GROUP BY parent_id
)
Dummy Data
You can test / play with both solutions using below dummy data
#standardSQL
WITH `parent` AS (
SELECT 1 id, 'Bob and Mary' married_couple, DATE '2010-01-01' married_at, 4 expected_kids UNION ALL
SELECT 2, 'Mick and Jo', DATE '2010-01-01', 4
),
`child` AS (
SELECT 1 id, 'Eddie' child_name, 1 parent_id, DATE '2012-01-01' date_of_birth UNION ALL
SELECT 2, 'Frankie', 1, DATE '2013-01-01' UNION ALL
SELECT 3, 'Robbie', 1, DATE '2014-01-01' UNION ALL
SELECT 4, 'Duncan', 1, DATE '2015-01-01' UNION ALL
SELECT 5, 'Rick', 2, DATE '2014-01-01'
)
Try the following query, whose logic is too verbose to explain it well. I join the parent and child tables, bringing into line the parent id, number of years elapsed since marriage, running number of children, and expected number of children. With this information in hand, we can easily find the first row whose running number of children matches or exceeds half of the expected number.
SELECT parent_id, num_years AS half_goal_reached
FROM
(
SELECT parent_id, num_years, cnt, expected_kids,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY num_years) rn
FROM
(
SELECT
t2.parent_id,
YEAR(t2.date_of_birth) - YEAR(t1.married_at) AS num_years,
(SELECT COUNT(*) FROM child c
WHERE c.parent_id = t2.parent_id AND
c.date_of_birth <= t2.date_of_birth) AS cnt,
t1.expected_kids
FROM parent t1
INNER JOIN child t2
ON t1.id = t2.parent_id
) t
WHERE
cnt >= expected_kids / 2
) t
WHERE t.rn = 1;
Note that there may be issues with how I computed the yearly differences, or how I compute the threshhold for half the number of expected children. Also, if we were using a recent enterprise database we could have used an analytic function to get the running number of children instead of a correlated subquery, but I was unsure if Big Query would support that, so I used the latter.
Apologies if this seems like a duplicate to this question but I believe my use case is slightly different.
I have two tables.
Table1
ID INTCODE
-----------------------------
000019827364 1
000019829201 2
890418392101 3
890418390395 4
890418398677 5
505586578932 6
505586578914 7
505586578933 8
505586578012 9
490201827383 10
490201827466 11
001952046578 12
Table2
INTCODE Category
-------------------------
1 Display
2 Display
3 Display
4 Display
5 Display
6 Audio
7 Audio
8 Audio
9 Audio
10 Audio
11 Audio
12 Audio
My expected query results are all possible 5 digit prefixes of each category and in each of these prefixes - I want to extract at least 2 full IDs. Below is an example if I had a where clause for category as 'Display'.
ID PREFIX Category ID
-----------------------------------------------
00001 Display 000019827364
00001 Display 000019829201
89041 Display 890418392101
89041 Display 890418390395
The query I currently have is
SELECT
SUBSTR(t1.ID, 1, 5)
FROM
table1 t1
,table2 t2
WHERE
AND UPPER(t2.category) = 'DISPLAY'
AND t2.REGION_ID = 1
AND t2.ZONE_ID = 2
AND t1.REGION_ID = 1
AND t1.ZONE_ID = 2
AND t1.INTCODE = t2.INTCODE
GROUP BY
SUBSTR(t1.ID, 1, 5)
I am now kind of lost. Should I be running another query where I say
t1.ID LIKE '00001%'
OR LIKE '89041%'
This list will go on to be huge cause some of the categories have 400-500 prefixes. Is there a better way to go about this? Possibly in a single query?
I'm using Oracle SQL.
Many thanks!
You can use row_number() for this:
select Category, ID, IDPrefix
from (select Category, ID, SUBSTR(ID, 1, 5) as IDPREFIX,
ROW_NUMBER() OVER (PARTITION BY SUBSTR(ID, 1, 5) ORDER BY ID) as seqnum
FROM table1 JOIN
table2 t2
ON t1.INTCODE = t2.INTCODE ANd
t1.Region_id = t2.Region_id and
t1.zone_id = t2.zone_id
WHERE UPPER(t2.category) = 'DISPLAY'
) t
WHERE seqnum <= 2;
Assuming you can to display two rows with different Id, without any more constraint, you could simply use an union where the first query would select the max id, and the second query the min id.
So your query would look something like this
select id_prefix, category, max(id)
from yourTable
union
select id_prefix, category, min(id)
from yourTable
Now simply add to this algorithm your where conditions.
I've been trying to solve this problem over the weekend, without luck so far. I have two tables:
TopOffers:
OfferId RetailerId Order
1 38 0
2 8 3
3 17 2
4 22 1
And Offers:
Id RetailerId Name Description etc...
1 3 Strawberry Red and smelly
2 38 Cookie Crunchy
3 17 Onion Of the nice kind
4 22 Apple Cheap
5 8 Toothbrush Lasts extra long!
My goal is to get the top 10 Offers for each Retailer ID. The order in which they should be listed is specified by the Order field in the TopOffer table (Sort order is Ascending). On top of that, the result should be padded to 10 offers when there are less than 10 TopOffer records for a retailer. The TopOffer table always contains 10 or less records per retailer.
So far I've managed to get this going, which works (I realize it doesn't get the top 10, but rather everything that's in the TopOffer table, which is alright, since the TopOffer table is always equal to or smaller than the top 10 for any retailer):
SELECT b.*
FROM
(
SELECT o.Id, to.`Order` FROM Offer AS o
LEFT JOIN TopOffer AS to
ON o.Id = to.OfferId
) AS a,
(
SELECT o.*, to.`Order` FROM Offer AS o
LEFT JOIN TopOffer AS to
ON o.Id = to.OfferId
) AS b
WHERE a.`Order` >= b.`Order` AND a.Id = b.Id
GROUP BY b.RetailerId, b.Id
HAVING Count(1) BETWEEN 1 AND 10
ORDER BY RetailerId, `Order` ASC
Unfortunately I can't seem to find any way of padding the result of this query with offers that don't have an entry in the TopOffer table if there aren't 10 TopOffer records for that retailer.
My sincerest thanks in advance for any help!
If you create a virtual table with numbers 1-10 you can left join to your results to get 10 of each
select number, results.*
from
(select 1 as number union select 2 union select 3 ... union select 10) numbers
left join
(your query here) results
on numbers.number = results.rank
Here for part ‘CF061W’ finum is 25, I will select records whose fparinum value is 25 now I will get these parts FA061W, HRD20600 and SD1201. Now again I will select records whose fparinum value is finumber of above retrieved parts FA061W, HRD20600 and SD1201 and so on. This should continue till the highest level (flevel), for the above table it is up to level 4.
Now I want single sql query that will retrieve all the records for the parent part ‘CF061W’.
Thanks in advance
Pradeep
this wil work for you
WITH TAB_CTE AS (
SELECT finum, part, fparinum, flevel
FROM TABTEST
WHERE PART='CF061W'
UNION ALL
SELECT e.finum, e.part, e.fparinum, e.flevel
FROM TABTEST e
INNER JOIN TAB_CTE ecte ON ecte.finum = e.fparinum
)
SELECT *
FROM TAB_CTE
OUTPUT
finum part fparinum flevel
25 CF061W 0 1
26 FA061w 25 2
27 hrd20600 25 2
35 sd1201 25 2
28 f1024 27 3
I might have the join condition columns: INNER JOIN PartHierarchy ph ON n.finum = ph.fparinum the wrong way round (not familiar with your schema).
WITH PartHierarchy (finum, part, fparinum , dsono, flevel) AS
(
-- Base case
SELECT
finum,
part,
fparinum,
dsono,
1 as flevel
FROM myTablename
WHERE fparinum = 0
UNION ALL
-- Recursive step
SELECT
n.finum,
n.part,
n.fparinum,
n.dsono,
ph.flevel + 1 AS flevel
FROM myTablename n
INNER JOIN PartHierarchy ph ON n.finum = ph.fparinum
)
SELECT *
FROM PartHierarchy
ORDER BY flevel
This is a classic recursive CTE (Common Table Expression)
This is almost a textbook example of when to use a Recursive CTE.
There are plenty of articles detailing what to do. eg. this one on MSDN:
http://msdn.microsoft.com/en-us/library/ms186243.aspx