I have a MySQL table that looks like this:
What I'm trying to find out is if column anaID has the value of 22 or 23, and no other value is assigned for anaID for that subID/sampleID combination.
something like
select * from anaData where subID='2020-04-21-17' and sampleID='crazy2'
and
(anaID=22 or anaID=23)
and
andID <> ??anything else??
I can query out all the matches and loop through them and have program logic that decides, but I'm hoping this can be done in a query.
You can use not exists:
select *
from anaData a
where subID = '2020-04-21-17' and
sampleID = 'crazy2' and
not exists (select 1
from anaData a2
where a2.subID = a.subID and a2.sampleID = a.sampleID and
a2.anaID not in (22, 23)
);
It turns out that you don't need anaID in (22, 23) in the outer query. The exists takes care of that.
This works if at least one anaID 22 or 23 exist:
select subID, sampleID
from anaData
where subID='2020-04-21-17'
and sampleID='crazy2'
group by subID, sampleID
having
sum(case when anaID in (22, 23) then 1 else -100 end) > 1
If you want both 22 and 23 switch to = 2 (assuming that the combination subID/sampleID/anaID is unique.
This can also be used without WHERE-conditions.
Related
How can we update multiple rows at once in case of below stated data
studentId = [1,2,5,7,9]
marks = [25, 22, 27, 30, 24]
what will be the MySQL statement, if we want to update studentId 1 with 25 marks, studentId 2 with 22 marks, studentId5 with 27 marks and so on.
Please note: Have to update in 1 query only.
Thanks in advance :)
If you are using MySql 8.0+ you can create a CTE that returns the rows of the ids that you want to update and their marks and join it to the table:
WITH cte(studentId, marks) AS (VALUES
ROW(1, 25), ROW(2, 22), ROW(5, 27), ROW(7, 30), ROW(9, 24)
)
UPDATE tablename t
INNER JOIN cte c ON c.studentId = t.studentId
SET t.marks = c.marks
See the demo.
For previous versions, instead of the CTE you can join a query that uses UNION ALL:
UPDATE tablename t
INNER JOIN (
SELECT 1 studentId, 25 marks UNION ALL
SELECT 2, 22 UNION ALL
SELECT 5, 27 UNION ALL
SELECT 7, 30 UNION ALL
SELECT 9, 24
) c ON c.studentId = t.studentId
SET t.marks = c.marks
See the demo.
You could run one simple query five times, with different values:
UPDATE MyTable SET marks = ? WHERE studentId = ?
The idea is that you would write a loop in some application code, so you process the first element from each of your arrays. Then the second element of both arrays, and so on. For example in PHP:
$studentId = [1,2,5,7,9];
$marks = [25, 22, 27, 30, 24];
$stmt = $pdo->prepare("UPDATE MyTable SET marks = ? WHERE studentId = ?");
for ($i=0; $i<5; ++$i) {
$stmt->execute([$marks[$i], $studentId[$i]]);
}
From its earliest versions, SQL was always intended to be used in combination with an application language. Other languages have variables and loops and conditions and functions, which complement SQL. The easiest solution is to use these languages together.
If you really want to write a single UPDATE statement to update all five, it's possible, but it's really not as clear.
UPDATE MyTable
SET marks = CASE studentId
WHEN 1 THEN 25
WHEN 2 THEN 22
WHEN 5 THEN 27
WHEN 7 THEN 30
WHEN 9 THEN 24
END
WHERE studentId IN (1,2,5,7,9);
There are other clever ways of doing it in one statement, but all these solutions are hard to modify or maintain. They are needlessly complex.
I recommend doing it the simple way.
Is there a way to GROUP BY using one column, but a set of values it could equal for a given group?
For instance, say I have the records:
id = 1, quantity = 40, type = 1
id = 2, quantity = 50, type = 2
id = 3, quantity = 20, type = 1
id = 4, quantity = 30, type = 3
id = 5, quantity = 60, type = 3
And I would like to group types 1 and 2 together, and group type 3s by themselves. Is there a simple way to do this in MySQL, or am I better off doing this after I get the results?
Sure, you can create a custom group using a CASE statement:
select case when id in (1,2)
then '1,2'
else id
end as custom_id_group
...
group by case when id in (1,2)
then '1,2'
else id
end
Edit:
Using the result of a CASE statement, via alias, in the GROUP BY clause isn't universally supported across RDBMSs, but more-recent versions of MySQL (>=5) will support it.
I suggest use two query and union the result
Maybe if you use something like "select *, if (type=1 or type=2, 'bigtype', type) as group_by_clause group by group_by_clause.
I didn't try it
You can do like this:
select count(*), if(type in (1, 2), 12, type) t from table group by t
Let's say I have a hypothetical table like so that records when some player in some game scores a point:
name points
------------
bob 10
mike 03
mike 04
bob 06
How would I get the sum of each player's scores and display them side by side in one query?
Total Points Table
bob mike
16 07
My (pseudo)-query is:
SELECT sum(points) as "Bob" WHERE name="bob",
sum(points) as "Mike" WHERE name="mike"
FROM score_table
You can pivot your data 'manually':
SELECT SUM(CASE WHEN name='bob' THEN points END) as bob,
SUM(CASE WHEN name='mike' THEN points END) as mike
FROM score_table
but this will not work if the list of your players is dynamic.
In pure sql:
SELECT
sum( (name = 'bob') * points) as Bob,
sum( (name = 'mike') * points) as Mike,
-- etc
FROM score_table;
This neat solution works because of mysql's booleans evaluating as 1 for true and 0 for false, allowing you to multiply truth of a test with a numeric column. I've used it lots of times for "pivots" and I like the brevity.
Are the player names all known up front? If so, you can do:
SELECT SUM(CASE WHEN name = 'bob' THEN points ELSE 0 END) AS bob,
SUM(CASE WHEN name = 'mike' THEN points ELSE 0 END) AS mike,
... so on for each player ...
FROM score_table
If you don't, you still might be able to use the same method, but you'd probably have to build the query dynamically. Basically, you'd SELECT DISTINCT name ..., then use that result set to build each of the CASE statements, then execute the result SQL.
This is called pivoting the table:
SELECT SUM(IF(name = "Bob", points, 0)) AS points_bob,
SUM(IF(name = "Mike", points, 0)) AS points_mike
FROM score_table
SELECT sum(points), name
FROM `table`
GROUP BY name
Or for the pivot
SELECT sum(if(name = 'mike',points,0)),
sum(if(name = 'bob',points,0))
FROM `table
you can use pivot function also for the same thing .. even by performance vise it is better option to use pivot for pivoting... (i am talking about oracle database)..
you can use following query for this as well..
-- (if you have only these two column in you table then it will be good to see output else for other additional column you will get null values)
select * from game_scores
pivot (sum(points) for name in ('BOB' BOB, 'mike' MIKE));
in this query you will get data very fast and you have to add or remove player name only one place
:)
if you have more then these two column in your table then you can use following query
WITH pivot_data AS (
SELECT points,name
FROM game_scores
)
SELECT *
FROM pivot_data
pivot (sum(points) for name in ('BOB' BOB, 'mike' MIKE));
I have the following table (id, Tag) with the following values
(1, 17)
(1, 31)
(2, 17)
(3, 31)
When I query the following
"SELECT id FROM table WHERE 1 AND Tag=17 AND Tag=31"
I expected it to return (id)
(1)
But it doesn't. (returns 0 row)
What's wrong here?
For any particular row the tag can't be both 17 and 31. You need
SELECT id
FROM table
WHERE Tag in (17, 31)
GROUP BY id
HAVING COUNT(DISTINCT Tag) = 2
Machines are logic and do what you asked them to do... your expectations are wrong in this case.
Tag 17 AND tag 31 conditions according to boolean math should be satisfied simultaneous to produce results.
You are asking for a row with the value of Tag being 17 AND 31...at the same time. You probably want this (Also, WHERE 1 is unneeded):
SELECT DISTINCT id FROM table WHERE Tag=17 OR Tag=31
EDIT: After reading some other answers, I decided to go crazy with this.
SELECT A.id
FROM table A JOIN table B ON A.id = B.id
WHERE A.Tag=17 AND B.Tag=31;
I have a table that consists of
id (auto_increment)
number int (can contain values from 10 to 12)
myvalue (varchar)
What I want to do is disperse the relational structure of this table for report purpose. I.e , I´d like to have something like:
id (auto_increment)
number10 (containing myvalue WHERE number=10)
number11 (containing myvalue WHERE number=11)
number12 (containing myvalue WHERE number=12)
I know that I can get the respective results by
SELECT myvalue FROM mytable WHERE number = 10;
but I haven´t figured out how to write these three SELECT statements into one single table or view.
thx for any help in advance!
Something like this maybe?:
SELECT
id,
IF(number=10, myvalue, NULL) AS number10,
IF(number=11, myvalue, NULL) AS number11,
IF(number=12, myvalue, NULL) AS number12
FROM mytable
This might do what you need. You've not explained it very well though so it might not!
SELECT user,
MIN(CASE WHEN number = 10 then myvalue end) AS number10,
MIN(CASE WHEN number = 11 then myvalue end) AS number11,
MIN(CASE WHEN number = 12 then myvalue end) AS number12
FROM table
WHERE number IN (10,11,12)
GROUP BY user
I don't get the "id number10 number11 number12" stuff, but if you want to select the rows with the number field matching a set of values, you can just do:
SELECT * FROM mytable WHERE number IN (10, 11, 12);
Or, alternatively, you can select a number range:
SELECT * FROM mytable WHERE number >= 10 AND number <= 12;
Edit 2:
Vin-G's got it. I was way off.