Sorry if the title is misleading, I don't really know the terminology for what I want to accomplish. But let's consider this table:
CREATE TABLE entries (
id INT NOT NULL,
number INT NOT NULL
);
Let's say it contains four numbers associated with each id, like this:
id number
1 0
1 9
1 17
1 11
2 5
2 8
2 9
2 0
.
.
.
Is it possible, with a SQL-query only, to count the numbers of matches for any two given numbers (tuples) associated with a id?
Let's say I want to count the number of occurrences of number 0 and 9 that is associated with a unique id. In the sample data above 0 and 9 does occur two times (one time where id=1 and one time where id=2). I can't think of how to write a SQL-query that solves this. Is it possible? Maybe my table structure is wrong, but that's how my data is organized right now.
I have tried sub-queries, unions, joins and everything else, but haven't found a way yet.
You can use GROUP BY and HAVING clauses:
SELECT COUNT(s.id)
FROM(
SELECT t.id
FROM YourTable t
WHERE t.number in(0,9)
GROUP BY t.id
HAVING COUNT(distinct t.number) = 2) s
Or with EXISTS():
SELECT COUNT(distinct t.id)
FROM YourTable t
WHERE EXISTS(SELECT 1 FROM YourTable s
WHERE t.id = s.id and s.id IN(0,9)
HAVING COUNT(distinct s.number) = 2)
Related
I have a table as below:
answer_id
user_id
date
1
22
2020-01
2
33
2020-02
3
78
2020-03
4
33
3030-04
what I need to do is to find the ratio of the new to returning users. By new, I mean the user_ids that were appeared only once, such as 22 and 78. And by returning users, I mean the user_ids that appeared more than once, such as 33.For example, the output should be something like this ----> 2:1
I need to do it with SQL and I though maybe I should create two temporary tables like the following:
with table_a as(
select user_id , count(answer_id)
from 1
group by 1
having count(answer_id)>1
)
table_b as (
select user_id , count(answer_id)
from b
group by 1
having count(answer_id)<2
)
the first table returns the user_ids that were repeated more than once, and the second table returns the number of ids that were unique. However, I am just lost what to do next. first time I tried it, I wanted to join them somehow, but I cannot join them as there aren't any matching field. Can any one help me on how I can find the ratio?
You just want to find out how many have a frequency of one and how many have a frequency higher than one. You can manipulate the output however you'd like later.
SELECT sum(vol=1) as newUsers,
sum(vol>1) as returning,
count(*) as total
FROM (
SELECT user_id, count(*) as vol
FROM table GROUP BY user_id
) z
I have 2 columns having users id participating in a transaction, source_id and destination_id. I'm building a function to sum all transactions grouped by any user participating on it, either as source or as destination.
The problem is, when I do:
select count (*) from transactions group by source_id, destination_id
it will first group by source, then by destination, I want to group them together. Is it possible using only SQL?
Sample Data
source_user_id destination_user_id
1 4
3 4
4 1
3 2
Desired result:
Id Count
4 - 3 (4 appears 3 times in any of the columns)
3 - 2 (3 appears 2 times in any of the columns)
1 - 2 (1 appear 2 times in any of the columns)
2 - 1 (1 appear 1 time in any of the columns)
As you can see on the example result, I want to know the number of times an id will appear in any of the 2 fields.
Use union all to get the id's into one column and get the counts.
select id,count(*)
from (select source_id as id from tbl
union all
select destination_id from tbl
) t
group by id
order by count(*) desc,id
edited to add: Thank you for clarifying your question. The following isn't what you need.
Sounds like you want to use the concatenate function.
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_concat
GROUP BY CONCAT(source_id,"_",destination_id)
The underscore is intended to distinguish "source_id=1, destination_id=11" from "source_id=11, destination_id=1". (We want them to be 1_11 and 11_1 respectively.) If you expect these IDs to contain underscores, you'd have to handle this differently, but I assume they're integers.
It may look like this.
Select id, count(total ) from
(select source_id as id, count (destination_user_id) as total from transactions group by source_id
union
select destination_user_id as id , count (source_id) as total from transactions group by destination_user_id ) q group by id
Problem Statement: I need my result set to include records that would not naturally return because they are NULL.
I'm going to put some simplified code here since my code seems to be too long.
Table Scores has Company_type, Company, Score, Project_ID
Select Score, Count(Project_ID)
FROM Scores
WHERE company_type= :company_type
GROUP BY Score
Results in the following:
Score Projects
5 95
4 94
3 215
2 51
1 155
Everything is working fine until I apply a condition to company_type that does not include results in one of the 5 score categories. When this happens, I don't have 5 rows in my result set any more.
It displays like this:
Score Projects
5 5
3 6
1 3
I'd like it to display like this:
Score Projects
5 5
4 0
3 6
2 0
1 3
I need the results to always display 5 rows. (Scores = 1-5)
I tried one of the approaches below by Spencer7593. My simplified query now looks like this:
SELECT i.score AS Score, IFNULL(count(*), 0) AS Projects
FROM (SELECT 5 AS score
UNION ALL
SELECT 4
UNION ALL
SELECT 3
UNION ALL
SELECT 2
UNION ALL
SELECT 1) i
LEFT JOIN Scores ON Scores.score = i.score
GROUP BY Score
ORDER BY i.score DESC
And gives the following results, which is accurate except that the rows with 1 in Projects should actually be 0 because they are derived by the "i". There are no projects with a score of 5 or 2.
Score Projects
5 1
4 5
3 6
2 1
1 3
Solved! I just needed to adjust my count to specifically look at the project count - count(project) rather than count(*). This returned the expected results.
If you always want your query to return 5 rows, with Score values of 5,4,3,2,1... you'll need a rowsource that supplies those Score values.
One approach would be to use a simple query to return those fixed values, e.g.
SELECT 5 AS score
UNION ALL SELECT 4
UNION ALL SELECT 3
UNION ALL SELECT 2
UNION ALL SELECT 1
Then use that query as inline view, and do an outer join operation to the results from your current query
SELECT i.score AS `Score`
, IFNULL(q.projects,0) AS `Projects`
FROM ( SELECT 5 AS score
UNION ALL SELECT 4
UNION ALL SELECT 3
UNION ALL SELECT 2
UNION ALL SELECT 1
) i
LEFT
JOIN (
-- the current query with "missing" Score rows goes here
-- for completeness of this example, without the query
-- we emulate that result with a different query
SELECT 5 AS score, 95 AS projects
UNION ALL SELECT 3, 215
UNION ALL SELECT 1, 155
) q
ON q.score = i.score
ORDER BY i.score DESC
It doesn't have to be the view query in this example. But there does need to be a rowsource that the rows can be returned from. You could, for example, have a simple table that contains those five rows, with those five score values.
This is just an example approach for the general approach. It might be possible to modify your existing query to return the rows you want. But without seeing the query, the schema, and example data, we can't tell.
FOLLOWUP
Based on the edit to the question, showing an example of the current query.
If we are guaranteed that the five values of Score will always appear in the Scores table, we could do conditional aggregation, writing a query like this:
SELECT s.score
, COUNT(IF(s.company_type = :company_type,s.project_id,NULL)) AS projects
FROM Scores s
GROUP BY s.score
ORDER BY s.score DESC
Note that this will require a scan of all the rows, so it may not perform as well. The "trick" is the IF function, which returns a NULL value in place of project_id, when the row would have been excluded by the WHERE clause.)
If we are guaranteed that project_id is non-NULL, we could use a more terse MySQL shorthand expression to achieve an equivalent result...
, IFNULL(SUM(s.company_type = :company_type),0) AS projects
This works because MySQL returns 1 when the comparison is TRUE, and otherwisee returns 0 or NULL.
Try something like this:
select distinct score
from (
select distinct score from scores
) s
left outer join (
Select Score, Count(Project_ID) cnt
FROM Scores
WHERE company_type= :company_type
) x
on s.score = x.score
Your posted query would not work without a group by statement. However, even there, if you don't have those particular scores for that company type, it wouldn't work either.
One option is to use an outer join. That would require a little more work though.
Here's another option using conditional aggregation:
select Score, sum(company_type=:company_type)
from Scores
group by Score
I am trying to list several products on a page. My query returns multiples of the same product and I am trying to figure out how to limit it to one only with my query.
The primary key on the first table that we will call table_one is ID.
The second table has a column of ProductID that references the primary key on table_one.
My query brings me back multiples of my ProductID that is equal to 6 below. I just want one result to be brought back, BUT I still want my all of my data in DateReserved on table_two to be queried. Pretty sure I need to add one more thing to my query, but I have not had much luck.
The results I want back are as follows.
ID Productname Quantity Image Date Reserved SumQuantity
6 productOne 6 'image.jpg' 03-31-2013 3
7 productTwo 1 'product.jpg' 04-04-2013 1
Here is my first table. table_one
ID Productname Quantity Image
6 productOne 6 'image.jpg'
7 productTwo 1 'product.jpg'
Here is my second table. table_two
ID ProductID DateReserved QuantityReserved
1 6 03-31-2013 3
2 6 04-07-2013 2
3 7 04-04-2013 1
Here is my query that I am trying to use.
SELECT *
FROM `table_one`
LEFT JOIN `table_two`
ON `table_one`.`ID` = `table_two`.`ProductID`
WHERE `table_one`.`Quantity` > 0
OR `table_two`.`DateReserved` + INTERVAL 5 DAY <= '2013-03-27'
ORDER BY ProductName
Sorry for posting another answer, but as it seems my first try on it was not so good ;)
To only get one result row per reservation you need to sum them up somehow.
First I suggest you explicitly select the columns you want back in your result and don't use "*".
I suggest you try something like this:
SELECT
`table_one`.`ID`, `table_one`.`Productname`, `table_one`.`Image`, `table_one`.`Quantity`,
`table_two`.`ProductID`, SUM(`table_two`.`QuantityReserved`)
FROM
`table_one`
LEFT JOIN
`table_two` ON `table_one`.`ID` = `table_two`.`ProductID`
WHERE
`table_one`.`Quantity` > 0
OR `table_two`.`DateReserved` + INTERVAL 5 DAY <= '2013-03-27'
GROUP BY `table_two`.`ProductID`
ORDER BY ProductName
As you see I used "SUM" to get a combined quantity, this is called aggregation and the "GROUP BY" helps you getting rid of multiple occurences of the same ProductID.
One problem that you have now is that you will have to get the reservation date from a seperate query (well at least I am now unsure how you would get it into the same query)
Since you are using MySQL
LIMIT <NUMBER>
should exactly do what you want, you just insert it after your ORDER BY clause, but probably you should also add one more ordering to that, so you can be sure that you will always get the one entity that you wanted and not just some "random" entity ;)
So without further ordering your query would look like this:
SELECT
*
FROM `table_one`
LEFT JOIN `table_two` ON `table_one`.`ID` = `table_two`.`ProductID`
WHERE
`table_one`.`Quantity` > 0
OR `table_two`.`DateReserved` + INTERVAL 5 DAY <= '2013-03-27'
ORDER BY ProductName
LIMIT 1
here some more description about that
SELECT a.member_id,a.member_name,a.gender,a.amount,b.trip_id,b.location
FROM tbl_member a
LEFT JOIN (SELECT trip_id, MAX(amount) as amount FROM tbl_member GROUP BY trip_id ) b ON a.trip_id= b.trip_id
LEFT JOIN tbl_trip b ON a.trip_id=c.trip_id
ORDER BY member_name
Here is a simplified version of my table:
group price spec
a 1 .
a 2 ..
b 1 ...
b 2
c .
. .
. .
I'd like to produce a result like this: (I'll refer to this as result_table)
price_a |spec_a |price_b |spec_b |price_c ...|total_cost
1 |. |1 |.. |... |
(min) (min) =1+1+...
Basically I want to:
select the rows containing the min price within each group
combine columns into a single row
I know this can be done using several queries and/or combined with some non-sql processing on the results, but I suspect that there maybe better solutions.
The reason that I want to do task 2 (combine columns into a single row)
is because I want to do something like the following with the result_table:
select *,
(result_table.total_cost + table1.price + table.2.price) as total_combined_cost
from result_table
right join table1
right join table2
This may be too much to ask for, so here is some other thoughts on the problem:
Instead of trying to combine multiple rows(task 2), store them in a temporary table
(which would be easier to calculate the total_cost using sum)
Feel free to drop any thoughts, don't have to be complete answer, I feel it's brilliant enough if you have an elegant way to do task 1 !
==Edited/Added 6 Feb 2012==
The goal of my program is to identify best combinations of items with minimal cost (and preferably possess higher utilitarian value at the same time).
Consider #ypercube's comment about large number of groups, temporary table seems to be the only feasible solution. And it is also pointed out there is no pivoting function in MySQL (although it can be implemented, it's not necessary to perform such operation).
Okay, after study #Johan's answer, I'm thinking about something like this for task 1:
select * from
(
select * from
result_table
order by price asc
) as ordered_table
group by group
;
Although looks dodgy, it seems to work.
==Edited/Added 7 Feb 2012==
Since there could be more than one combination may produce the same min value, I have modified my answer :
select result_table.* from
(
select * from
(
select * from
result_table
order by price asc
) as ordered_table
group by group
) as single_min_table
inner join result_table
on result_table.group = single_min_table.group
and result_table.price = single_min_table.price
;
However, I have just realised that there is another problem I need to deal with:
I can not ignore all the spec, since there is a provider property, items from different providers may or may not be able to be assembled together, so to be safe (and to simplify my problem) I decide to combine items from the same provider only, so the problem becomes:
For example if I have an initial table like this(with only 2 groups and 2 providers):
id group price spec provider
1 a 1 . x
2 a 2 .. y
3 a 3 ... y
4 b 1 ... y
5 b 2 x
6 b 3 z
I need to combine
id group price spec provider
1 a 1 . x
5 b 2 x
and
2 a 2 .. y
4 b 1 ... y
record (id 6) can be eliminated from the choices since it dose not have all the groups available.
So it's not necessarily to select only the min of each group, rather it's to select one from each group so that for each provider I have a minimal combined cost.
You cannot pivot in MySQL, but you can group results together.
The GROUP_CONCAT function will give you a result like this:
column A column B column c column d
groups specs prices sum(price)
a,b,c some,list,xyz 1,5,7 13
Here's a sample query:
(The query assumes you have a primary (or unique) key called id defined on the target table).
SELECT
GROUP_CONCAT(a.`group`) as groups
,GROUP_CONCAT(a.spec) as specs
,GROUP_CONCAT(a.min_price) as prices
,SUM(a.min_prices) as total_of_min_prices
FROM
( SELECT price, spec, `group` FROM table1
WHERE id IN
(SELECT MIN(id) as id FROM table1 GROUP BY `group` HAVING price = MIN(price))
) AS a
See: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html
Producing the total_cost only:
SELECT SUM(min_price) AS total_cost
FROM
( SELECT MIN(price) AS min_price
FROM TableX
GROUP BY `group`
) AS grp
If a result set with the minimum prices returned in row (not in column) per group is fine, then your problem is of the gretaest-n-per-group type. There are various methods to solve it. Here's one:
SELECT tg.grp
tm.price AS min_price
tm.spec
FROM
( SELECT DISTINCT `group` AS grp
FROM TableX
) AS tg
JOIN
TableX AS tm
ON
tm.PK = --- the Primary Key of the table
( SELECT tmin.PK
FROM TableX AS tmin
WHERE tmin.`group` = tg.grp
ORDER BY tmin.price ASC
LIMIT 1
)