I've got a "parameters" table like this:
id | job_id | value
1 | 100 | value1
2 | 100 | value2
3 | 100 | value3
4 | 200 | value4
5 | 200 |
6 | 200 | value7
Then, for parameters that have list values, I've got a second, list_param_items table:
id | param_id | value
1 | 5 | value5
2 | 5 | value6
I'm trying to figure out a query that would allow me to get id, job_id, and value columns, containing data from the parameters table, except when a row in the parameters table has rows in the list_param_items table with its id, concatenate those to form its value column.
So if I were to select all parameters, I'd want to get this:
id | job_id | value
1 | 100 | value1
2 | 100 | value2
3 | 100 | value3
4 | 200 | value4
5 | 200 | value5,value6
6 | 200 | value7
I also need to be able to select all params for a given job ID and get the same data, just for that job. So, for job_id = 200:
id | job_id | value
4 | 200 | value4
5 | 200 | value5,value6
6 | 200 | value7
I've accomplished it with this query:
SELECT * FROM (
SELECT parameters.id, parameters.job_id, GROUP_CONCAT(list_param_items.value) AS value
FROM jobdetail_input
INNER JOIN list_param_items
ON list_param_items.param_id = parameters.id
WHERE parameters.job_id = 200
UNION
SELECT parameters.* FROM parameters
WHERE parameters.job_id = 200
) unmerged
GROUP BY id;
The problem with this is that, in reality, there are many more columns in the parameter table than I've shown here. So I'd prefer a way to do it without having to list out all the column names. The reason I had to list them all out was to eliminate the "value" column from parameters in the first SELECT statement, so that both sides of the UNION would have the same set of columns to work with. I'd like to not list them out so the query wouldn't have to be updated anytime a column is added.
The other question I have about how efficient that query is. Even if I have to list all the parameters table columns out, is there a better way to do it?
Join the first table to a subquery of the second table which aggregates the values into a CSV string:
SELECT
p.id,
p.job_id,
COALESCE(p.value, lpi.value) AS value
FROM parameters p
LEFT JOIN
(
SELECT param_id, GROUP_CONCAT(value ORDER BY value) value
FROM list_param_items
GROUP BY param_id
) lpi
ON p.id = lpi.param_id
ORDER BY
p.id;
Demo
Related
I'm trying to get the last record, but I ger the first record.
What am I doing wrong?
My Table permission
|id|pid|uid|
| 1| 2 | 2 |
| 2| 5 | 2 |
My Table fruits
|id|pid|number1|number2|
|1 | 1 | 50 | 100 |
|2 | 1 | 10 | 100 |
|3 | 1 | 100 | 100 | <== Try get last record
I want get the last record, but I can't.
I create the query, but not work:
SELECT DISTINCT(fruits.pid), permission.pid, fruits.number1, fruits.number2
FROM permission
LEFT JOIN fruits ON permission.pid = fruits.pid
WHERE permission.uid = '2'
GROUP BY fruits.pid
ORDER BY fruits.id DESC
I need the result:
|pid|pid|number1|number2|
|3 | 1 | 100 | 100 |
Your join doesn't join any rows. The value of fruits.pid is always 1. The values of permissions.pid are (2, 5). Thus, your join of fruits.pid = permission.pid doesn't find any rows that match, so you're not getting the results you expect. If you drop the DISTINCT in the query and remove the GROUP BY (which causes it to fail in MySQL 8) your query produces:
pid pid number1 number2
null 2 null null
null 5 null null
The row you want isn't in the result set, so of course you don't get it.
The other problem you have is that the number 3 is not in the column fruits.pid. It's an id value, so I suspect you're joining on the wrong field. And you've got permission.pid as the second field returned by your query, but that column only contains 2 and 5, as noted earlier, but you want a value of 1 there; thus, it appears you want to return fruits.pid as the second column of the result set. So something like:
SELECT fruits.id, fruits.pid, fruits.number1, fruits.number2
FROM fruits
LEFT JOIN permission
ON permission.id = fruits.pid
WHERE permission.uid = '2'
ORDER BY fruits.id DESC
db<>fiddle here
I need to retrieve rows that have a numeric or null value in the HomeID column and finally return the value with a numeric value if there is a row with the same symbol
// my table
+--------+---------+-------+
| Symbol | Home ID | Value |
+--------+---------+-------+
| test | 1 | value |
| test | NULL | value |
| test1 | 2 | value |
| test2 | 3 | vlaue |
+--------+---------+-------+
Actually, I did something like that. It added up the symbols for me, but I don't know how to return the poem I need
SELECT
[Symbol],
COUNT(*) AS CNT
FROM [DB].[dbo].[Table]
GROUP BY
[Symbol]
HAVING COUNT(*) > 1;
One method is:
select t.*
from t
where t.homeid is not null or
not exists (select 1
from t t2
where t2.symbol = t.symbol and t2.homeid is not null
);
You can also do this with aggregation, if you just have these three columns and you want exactly one row per symbol:
select symbol, max(homeid) as homeid,
coalesce(max(case when homeid is not null then value end),
max(value)
) as value
from t
group by symbol;
class_table
+----+-------+--------------+
| id |teac_id| student_id |
+----+-------+--------------+
| 1 | 1 | 1,2,3,4 |
+----+-------+--------------+
student_mark
+----+----------+--------+
| id |student_id| marks |
+----+----------+--------+
| 1 | 1 | 12 |
+----+----------+--------+
| 2 | 2 | 80 |
+----+----------+--------+
| 3 | 3 | 20 |
+----+----------+--------+
I have these two tables and i want to calculate the total marks of student and my sql is:
SELECT SUM(`marks`)
FROM `student_mark`
WHERE `student_id` IN
(SELECT `student_id` FROM `class_table` WHERE `teac_id` = '1')
But this will return null, please help!!
DB fiddle
Firstly, you should never store comma separated data in your column. You should really normalize your data. So basically, you could have a many-to-many table mapping teacher_to_student, which will have teac_id and student_id columns.
In this particular case, you can utilize Find_in_set() function.
From your current query, it seems that you are trying to getting total marks for a teacher (summing up marks of all his/her students).
Try:
SELECT SUM(sm.`marks`)
FROM `student_mark` AS sm
JOIN `class_table` AS ct
ON FIND_IN_SET(sm.`student_id`, ct.`student_id`) > 0
WHERE ct.`teac_id` = '1'
In case, you want to get total marks per student, you would need to add a Group By. The query would look like:
SELECT sm.`student_id`,
SUM(sm.`marks`)
FROM `student_mark` AS sm
JOIN `class_table` AS ct
ON FIND_IN_SET(sm.`student_id`, ct.`student_id`) > 0
WHERE ct.`teac_id` = '1'
GROUP BY sm.`student_id`
Just in case you want to know why, The reason it returned null is because the subquery returned as '1,2,3,4' as a whole. What you need is to make it returned 1,2,3,4 separately.
What your query returned
SELECT SUM(`marks`)
FROM `student_mark`
WHERE `student_id` IN ('1,2,3,4')
What you expect is
SELECT SUM(`marks`)
FROM `student_mark`
WHERE `student_id` IN (1,2,3,4)
The best way is it normalize as #madhur said. In your case you need to make the teacher and student as one to many link
+----+-------+--------------+
| id |teac_id| student_id |
+----+-------+--------------+
| 1 | 1 | 1 |
+----+-------+--------------+
| 2 | 1 | 2 |
+----+-------+--------------+
| 3 | 1 | 3 |
+----+-------+--------------+
| 4 | 1 | 4 |
+----+-------+--------------+
If you want to filter your table based on a comma separated list with ID, my approach is to
append extra commas at the beginning and at the end of a list as well as at the beginning and at the end of an ID, eg.
1 becomes ,1, and list would become ,1,2,3,4,. The reason for that is to avoid ambigious matches like 1 matches 21 or 12 in a list.
Also, EXISTS is well-suited in that situation, which together with INSTR function should work:
SELECT SUM(`marks`)
FROM `student_mark` sm
WHERE EXISTS(SELECT 1 FROM `class_table`
WHERE `teac_id` = '1' AND
INSTR(CONCAT(',', student_id, ','), CONCAT(',', sm.student_id, ',')) > 0)
Demo
BUT you shouldn't store related IDs in one cell as comma separated list - it should be foreign key column to form proper relation. Joins would become trivial then.
I have a similar table to this in SQL:
id |tag | entryID
---+----+--------
1 |foo | 0
2 |foo | 0
3 |bar | 3
5 |bar | 3
6 |foo | 3
7 |foo | 3
I want to run a query to count distinct rows in the table (with the id column dropped). The result should look like this (or a transpose of this table):
(tag=foo, entryID=0) | (tag=foo, entryID=3) | (tag=bar, entryID=3)
---------------------+----------------------+---------------------
2 | 2 | 2
What should this query look like?
Note: The values in each of the columns are not known beforehand.
You can do this using conditional aggregation:
select sum(tag = 'foo' and entryId = 0) as "tag=foo, entryID=0",
sum(tag = 'foo' and entryId = 3) as "tag=foo, entryID=3",
sum(tag = 'bar' and entryId = 3) as "tag=bar, entryID=0"
from t;
However, the normal method is to put the counts in rows, not columns:
select tag, entryId, count(*)
from t
group by tag, entryId;
The rows are much more versatile, because you don't have to list out every combination you might want to match.
Ok so its easier to give an example and hopefully some has a solution:
I have table that holds bids:
ID | companyID | userID | contractID | bidAmount | dateAdded
Below is an example set of rows that could be in the table:
ID | companyID | userID | contractID | bidAmount | dateAdded
--------------------------------------------------------------
10 | 2 | 1 | 94 | 1.50 | 1309933407
9 | 2 | 1 | 95 | 1.99 | 1309933397
8 | 2 | 1 | 96 | 1.99 | 1309933394
11 | 103 | 1210 | 96 | 1.98 | 1309947237
12 | 2 | 1 | 96 | 1.97 | 1309947252
Ok so what I would like to do is to be able to get all the info (like by using * in a normal select statement) the lowest bid for each unique contractID.
So I would need the following rows:
ID = 10 (for contractID = 94)
ID = 9 (for contractID - 95)
ID = 12 (for contractID = 96)
I want to ignore all the others. I thought about using DISTINCT, but i haven't been able to get it to return all the columns, only the column I'm using for distinct.
Does anyone have any suggestions?
Thanks,
Jeff
select *
from mytable main
where bidAmount = (
select min(bidAmount)
from mytable
where contractID = main.contractID)
Note that this will return multiple rows if there is more than one record sharing the same minimum bid.
Didn't test it but it should be possible with this query although it might not be really fast:
SELECT * FROM bids WHERE ID IN (
SELECT ID FROM bids GROUP BY contractID ORDER BY MIN(bidAmount) ASC
)
This would be the query for MySQL, maybe you need to adjust it for another db.
You could use a subquery to find the lowest rowid per contractid:
select *
from YourTable
where id in
(
select min(id)
from YourTable
group by
ContractID
)
The problem is that distinct does not return a specific row - it return distinct values, which ( by definition ) could occur on multiple rows.
Subqueries are your answer, and somewhere in the suggestions above is probably the answer. Your subquery need to return the ids or the rows with the minimum bidvalue. Then you can select * from the rows with those ids.