I'm trying to do something that should be relatively simpel, I guess, but I can't wrap my head around it.
I want to find a certain id in a table, and then select the next X rows that meet a certain criteria.
Example table:
+++++++++++++++++++++++++++++++
| id | type | value | sorting |
+++++++++++++++++++++++++++++++
|1 | 'x' | 'foo' | 1 |
|2 | 'y' | 'bar' | 5 |
|3 | 'z' | 'foo2'| 9 |
|4 | 'z' | 'bar2'| 29 |
|5 | 'x' | 'foo3'| 3 |
|6 | 'z' | 'bar3'| 11 |
|7 | 'z' | 'foo4'| 4 |
+++++++++++++++++++++++++++++++
What I want is to select the next X rows where type = 'z' starting from row with id = 3, sorted by sorting.
So for this table, I would want to get rows with id 3, 6, 4, in that order. Note that row with id 7 matches the type, but it's sorting value is lower than row with id 3.
Can this be done in one query?
My ideas: for the example table, something as such would do:
SELECT * FROM table WHERE type = 'z' AND sorting > 9 ORDER BY sorting LIMIT 3, X
But obviously, I can't know the values 9 and the offset yet, so I'd need a way to:
Find the sorting value for id 3.
Find the offset for id 3.
Apply those to the query.
Edit: Where I'm at now:
SELECT
*
FROM table
WHERE type = 'z' AND sorting > (SELECT sorting FROM table WHERE id = 3)
ORDER BY sorting
LIMIT 0, 25
So the last step is to get the offset of 3 and pass that as the first parameter to LIMIT.
Your goal is still not clear. But here is my attempt:
http://sqlfiddle.com/#!9/2d389/1
SELECT t1.*
FROM foobar t1
JOIN (SELECT *
FROM foobar
WHERE id=3
) t2
ON t1.id >= t2.id
AND t1.type = t2.type
AND t1.sorting >= t2.sorting
ORDER BY sorting
LIMIT 10
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 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.
This is a problem for which I have a working query, but it feels horribly inefficient to me and I'd like some help constructing a better one. This is going into a live production environment, and the number of queries the db handles each day is incredibly high, so the more efficient this can be, the better. I have a table structured something like this (stripped to just the relevant parts):
id | type | datecolumn
1 | A | 2014-01-01
1 | B | 0000-00-00
2 | A | 2014-01-02
2 | B | 2014-01-10
3 | A | 2014-01-01
3 | B | 0000-00-00
There will always be two rows for each id, one of type A and one of type B. A will always have a valid date, and B will either have a date >= that of A, or all 0s. What I want is a query that will produce output similar to this:
id | date for A | date for B
1 | 2014-01-01 | None
2 | 2014-01-02 | 2014-01-10
3 | 2014-01-01 | None
The way I'm doing this now is as follows:
SELECT
id,
IF(MIN(datecolumn) > 0, MIN(datecolumn), MAX(datecolumn)) AS 'date for A',
IF(MIN(datecolumn) > 0, MAX(datecolumn), 'None') AS 'date for B'
GROUP BY id
But it really feels like I should be able to pluck the datecolumn value on a by-type basis somehow. I know the simplest solution should be to change the table structure so that each id only uses one row, but I'm afraid that is not possible in this case; there has to be two rows. Is there a way to leverage the type column properly in this query?
Edit: Also, this is on a table that will have upwards of 10,000,000 rows. So again, efficiency is key.
I'd stick with what you've go, but maybe write it this way...
CREATE TABLE my_table
(id INT NOT NULL
,type CHAR(1) NOT NULL
,datecolumn DATE NOT NULL DEFAULT '0000-00-00'
,PRIMARY KEY(id,type)
);
INSERT INTO my_table VALUES
(1 ,'A','2014-01-01'),
(1 ,'B','0000-00-00'),
(2 ,'A','2014-01-02'),
(2 ,'B','2014-01-10'),
(3 ,'A','2014-01-01'),
(3 ,'B','0000-00-00');
SELECT id
, MAX(CASE WHEN type = 'A' THEN datecolumn END) a
, MAX(REPLACE(CASE WHEN type='B' THEN datecolumn END,'0000-00-00','none')) b
FROM my_table
GROUP
BY id;
+----+------------+------------+
| id | a | b |
+----+------------+------------+
| 1 | 2014-01-01 | none |
| 2 | 2014-01-02 | 2014-01-10 |
| 3 | 2014-01-01 | none |
+----+------------+------------+
Make sure you have an index that covers both the id and type columns (e.g ALTER TABLE tbl ADD INDEX (type,id)), then do:
SELECT
table_a.id,
table_a.datecolumn AS 'date for A',
IF(table_b.datecolumn > 0, table_b.datecolumn, 'None') AS 'date for B'
FROM tbl AS table_a
JOIN tbl AS table_b ON table_a.id = table_b.id AND table_b.type = 'B'
WHERE table_a.type = 'A';
I have a table like this:
startyearmonthday| id
20130901 | 1
20131004 | 2
20130920 | 3
20131105 | 4
20131009 | 5
I want to write a query where I can return a table like this:
startyearmonthday| id | endyearmonthday
20130901 | 1 | 20130920
20130920 | 3 | 20131004
20131004 | 2 | 20131009
20131009 | 5 | 20131105
20131105 | 4 | null
So I want the end date based on the next earliest start date after the current start date. I imagine some sort of join is involved but I can't figure it out...
I would be inclined to do this with a correlated subquery:
select t.*,
(select startyearmonthday
from t t2
where t2.startyearmonthday > t.startyearmonthday
order by t2.startyearmonthday
limit 1
) as endyearmonthday
from t;
As with your earlier question, this will run pretty quickly with an index on t(startyearmonthday).
Try this (assuming there are no repeated rows):
select a.*, b.startyearmonthday as endyearmonthday
from table_name a
left join table_name b
on b.startyearmonthday > a.startyearmonthday and not exists(select startyearmonthday from table_name c where a.startyearmonthday < c.startyearmonthday and c.startyearmonthday < b.startyearmonthday)
I have a table with the format:
Id | Loc |
-------|-----|
789-A | 4 |
123 | 1 |
123-BZ | 1 |
123-CG | 2 |
456 | 2 |
456 | 3 |
789 | 4 |
I want to exclude certain rows from the result of query based on whether a duplicate exists. In this case, though, the definition of a duplicate row is pretty complex:
If any row returned by the query (let's refer to this hypothetical row as ThisRow) has a counterpart row also contained within the query results where Loc is identical to ThisRow.Loc AND Id is of the form <ThisRow.Id>-<an alphanumeric suffix> then ThisRow should be considered a duplicate and excluded from the query results.
For example, using the table above, SELECT * FROM table should return the results set below:
Id | Loc |
-------|-----|
789-A | 4 |
123-BZ | 1 |
123-CG | 2 |
456 | 2 |
456 | 3 |
I understand how to write the string matching conditional:
ThisRow.Id REGEXP '^PossibleDuplicateRow.Id-[A-Za-z0-9]*'
and the straight comparison of Loc:
ThisRow.Loc = PossibleDuplicateRow.Loc
What I don't understand is how to form these conditionals into a (self-referential?) query.
Here's one way:
SELECT *
FROM myTable t1
WHERE NOT EXISTS
(
SELECT 1
FROM myTable t2
WHERE t2.Loc = t1.Loc
AND t2.Id LIKE CONCAT(t1.Id, '-%')
)
SQL Fiddle example
Or, the same query using an anti-join (which should be a little faster):
SELECT *
FROM myTable t1
LEFT OUTER JOIN myTable t2
ON t2.Loc = t1.Loc
AND t2.Id LIKE CONCAT(t1.Id, '-%')
WHERE t2.Id IS NULL
SQL Fiddle example
In the example data you give, there are no examples of duplicate locs not being on duplicate rows. For example, you don't have a row "123-AZ, 1", where the prefix row "123, 1" would conflict with two rows.
If this is a real characteristic of the data, then you can eliminate dups without a self join, by using aggregation:
select max(id), loc
from t
group by (case when locate(id, '-') = 0 then id
else left(id, locate(id, '-') - 1)
end), loc
I offer this because an aggregation should be much faster than a non-equijoin.