Preserve from splitting results - mysql

Is there any possible way to SELECT from MYSQL database and preserve from splitting results? I'd like to get all the data from previous day, but it'll be too much, but I also cannot split results:
Select all with certain limit, but do not split (by certain value, i.e. user_id) onto separate results.
EXAMPLE
SELECT
ti.id, ti.date, ti.duedate, ti.datepaid,
tii.invoiceid, tii.userid,
tc.postcode, tc.country,
(SELECT GROUP_CONCAT(value) FROM custom WHERE relid=tc.id) AS vatid
FROM invoices ti
LEFT JOIN invoiceitems tii
ON tii.invoiceid=ti.id
LEFT JOIN clients tc
ON tc.id=tii.userid
WHERE ti.status='Paid'
AND ti.nullmo_no IS NULL
ORDER BY tii.userid AND tii.id
Now I get all the results, but I need to split them without breaking userid. For example one SELECT returns 20 results, because there were 15 invoices for user 1, and 5 invoices for user 2, then the next call returns the rest, also with a limit, but not breaking user related group of results:
SELECT
part 1 (all from user 1, all from user 2)
part 2 (all from user 3, all from user 4)
Can this be done in one select statement?
id = 1,2,3,4,5,6,7,8,9,10
name = n1, n2, n3, n4, n5, n6, n7, n8, n9, n10
user_id = 1, 1, 1, 2, 2, 2, 3, 3, 4, 5 // split but not divide
content = c1,c2,c3,c4,c5,c6,c7,c8,c9,c10
date = yesterday, yesterday, yesterday, yesterday, yesterday, yesterday, yesterday, yesterday, yesterday, yesterday
The deal is to select all of them, with a limit, but not to split user_id, so: 1. All from yesterday 2. LIMIT if per one or more user_id's there are more results than LIMIT So the limit would be determined by the number of results.

Related

How to find out next available number

In my MySQL table I have field called sequence where I have values like
1 , 2, 3, 5, 6, 7, 8, 10 some of the sequence number are skiped due to deleted records. How do I find out next available number from given number. let's say if I need next number from 3 , how do I get number 5 as my next number in sequence not the 4.
To find out the next ID after 3 that appears in your table, you should do
SELECT id FROM thetable WHERE id>3 ORDER BY id ASC LIMIT 1
This just considers IDs that are greater than 3, in ascending order, and then takes the first one on that list. If it returns you one result, then that's the next one used in the table; if it doesn't return a result at all, then the ID you gave it was already the highest one in the table (or, strictly speaking, at least as high as the highest one in the table).
If you want a general expression that works to get the next available number, then you can use an aggregation query:
select coalesce(max(id), maxid + 1) as NextAvailableId
from table t cross join
(select max(id) as maxid from table t) x
where id > 3;
Or, if you don't like the cross join, you can use conditional aggregation:
select coalesce(max(case when id > 3 then id end), max(id)) as NextAvailableId
from table t;

searching for records in mysql using or - and - not in query

I think I am getting turned around when looking at this. I am trying to get all patron records relating to transactions that have a transaction item with one of a number of ids (1 or 2) as well as transaction items with other ids (3 or 4) but not with transaction items with other ids (5 or 6)
The structure is:
=patron=
id
fname
lname
email
phone
=trans=
id
id_org
id_patron
=trans_item=
id
id_trans
id_perf
I was trying the following:
SELECT
patron.email,
patron.fname,
patron.lname,
patron.phone
FROM
trans_item,
trans,
patron
WHERE
trans_item.id_perf IN (1,2)
AND
trans_item.id_perf IN (3,4)
AND
trans_item.id_perf NOT IN (5,6)
AND
trans_item.id_trans = trans.id
AND
trans.id_org = 1
AND
trans.id_patron = patron.id
GROUP BY
patron.id
ORDER BY
patron.email DESC,
patron.phone DESC
I'm aware that saying the id needs to be 2 AND 4 is always going to return nothing but I need to have it as if id is in (1,2) AND (3,4) so it can be 1 or 2 but also needs to be in 3 or 4
For Clarity:
I am trying to get patrons who have gone to performance 1 OR 2 and 3 OR 4 but NOT 5 OR 6
You can do this with group by and having. The basic idea is:
select ti.id_trans
from trans_item ti
group by ti.id_trans
having sum(ti.id_perf in (1, 2)) > 0 and
sum(ti.id_perf in (3, 4)) > 0 and
sum(ti.id_perf in (5, 6)) = 0;
Each condition in the having clause checks a row for the particular ids. The > 0 means they exist for transaction. The = 0 means they do not.
If you want additional columns from other tables, you can join back to this result set.
I think I have a solution. If I combine the ids for all perfs and group all results by the trans_item.id I can get a list that has duplicates. I then convert them into a php multidimensional array and exclude / include based on the ids for each requirement finding the duplicates that way. Any other suggestions are welcome

Advanced (probably not?) MYSQL query select statement

Please see the below sample data:
'messages' table data:
id, date, user, seen
674, 1399430687, 2, 0
675, 1399430957, 2, 1399431766
676, 1399431065, 1, 1399431766
677, 1399431723, 2, 1399431766
678, 1399434322, 2, 0
679, 1399434330, 2, 0
I want to do a count of only the most recent non seen (seen=0) records to a particular user (user: 2) UP and ONLY UNTIL the next seen record (seen>0). So in the above case there are 3 unseen (seen=0) records to user (user: 2) but only 2 of them I am interested as the next 1 has some seen records in between it. So in the above scenario count of 2 would be returned (not the actual records).
Also if the most recent record to user (user: 2) is seen (seen>0) then count of 0 should be returned. I just can't get my head around how to do this in mysql, or how to really describe it.
This is all I can work out, but in the above scenario this will result in 3, not 2:
SELECT COUNT( * )
FROM `messages`
WHERE seen = 0
AND user = 2
ORDER BY DATE DESC
I hope this makes sense.
Thanks.
You need to find the latest non-zero seen data, and then filter after that.
SELECT Count(id) FROM data WHERE user = 3 AND seen = 0
AND date > (
SELECT coalesce(MAX(date),0) FROM data WHERE seen <> 0 AND user = 3
);
here's the fiddle
EDIT:
The previous code wouldn't return a correct answer if the user hadn't seen any records yet at all, this code fixes it by returning 0 instead of null.

mysql distribution of combinations/values

I have a mysql table which contains some random combination of numbers. For simplicity take the following table as example:
index|n1|n2|n3
1 1 2 3
2 4 10 32
3 3 10 4
4 35 1 2
5 27 1 3
etc
What I want to find out is the number of times a combination has occured in the table. For instance, how many times has the combination of 4 10 or 1 2 or 1 2 3 or 3 10 4 etc occured.
Do I have to create another table that contains all possible combinations and do comparison from there or is there another way to do this?
For a single combination, this is easy:
SELECT COUNT(*)
FROM my_table
WHERE n1 = 3 AND n2 = 10 AND n3 = 4
If you want to do this with multiple combinations, you could create a (temporary) table of them and join that table with you data, something like this:
CREATE TEMPORARY TABLE combinations (
id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
n1 INTEGER, n2 INTEGER, n3 INTEGER
);
INSERT INTO combinations (n1, n2, n3) VALUES
(1, 2, NULL), (4, 10, NULL), (1, 2, 3), (3, 10, 4);
SELECT c.n1, c.n2, c.n3, COUNT(t.id) AS num
FROM combinations AS c
LEFT JOIN my_table AS t
ON (c.n1 = t.n1 OR c.n1 IS NULL)
AND (c.n2 = t.n2 OR c.n2 IS NULL)
AND (c.n3 = t.n3 OR c.n3 IS NULL)
GROUP BY c.id;
(demo on SQLize)
Note that this query as written is not very efficient due to the OR c.n? IS NULL clauses, which MySQL isn't smart enough to optimize. If all your combinations contain the same number of terms, you can leave those out, which will allow the query to make use of indexes on the data table.
Ps. With the query above, the combination (1, 2, NULL) won't match (35, 1, 2). However, (NULL, 1, 2) will, so, if you want both, a simple workaround would be to just include both patterns in your table of combinations.
If you actually have many more columns than shown in your example, and you want to match patterns that occur in any set of consecutive columns, then your really should pack your columns into a string and use a LIKE or REGEXP query. For example, if you concatenate all your data columns into a comma-separated string in a column named data, you could search it like this:
INSERT INTO combinations (pattern) VALUES
('1,2'), ('4,10'), ('1,2,3'), ('3,10,4'), ('7,8,9');
SELECT c.pattern, COUNT(t.id) AS num
FROM combinations AS c
LEFT JOIN my_table AS t
ON CONCAT(',', t.data, ',') LIKE CONCAT('%,', c.pattern, ',%')
GROUP BY c.id;
(demo on SQLize)
You could make this query somewhat faster by making the prefixes and suffixes added with CONCAT() part of the actual data in the tables, but this is still going to be a fairly inefficient query if you have a lot of data to search, because it cannot make use of indexes. If you need to do this kind of substring searching on large datasets efficiently, you may want to use something better suited for than specific purpose than MySQL.
You only have three columns in the table, so you are looking for combinations of 1, 2, and 3 elements.
For simplicity, I'll start with the following table:
select index, n1 as n from t union all
select index, n2 from t union all
select index, n3 from t union all
select distinct index, -1 from t union all
select distinct index, -2 from t
Let's call this "values". Now, we want to get all triples from this table for a given index. In this case, -1 and -2 represent NULL.
select (case when v1.n < 0 then NULL else v1.n end) as n1,
(case when v2.n < 0 then NULL else v2.n end) as n2,
(case when v3.n < 0 then NULL else v3.n end) as n3,
count(*) as NumOccurrences
from values v1 join
values v2
on v1.n < v2.n and v1.index = v2.index join
values v3
on v2.n < v3.n and v2.index = v3.index
This is using the join mechanism to generate the combinations.
This method finds all combinations regardless of ordering (so 1, 2, 3 is the same as 2, 3, 1). Also, this ignores duplicates, so it cannot find (1, 2, 2) if 2 is repeated twice.
SELECT
CONCAT(CAST(n1 AS VARCHAR(10)),'|',CAST(n2 AS VARCHAR(10)),'|',CAST(n3 AS VARCHAR(10))) AS Combination,
COUNT(CONCAT(CAST(n1 AS VARCHAR(10)),'|',CAST(n2 AS VARCHAR(10)),'|',CAST(n3 AS VARCHAR(10)))) AS Occurrences
FROM
MyTable
GROUP BY
CONCAT(CAST(n1 AS VARCHAR(10)),'|',CAST(n2 AS VARCHAR(10)),'|',CAST(n3 AS VARCHAR(10)))
This creates a single column that represents the combination of the values within the 3 columns by concatenating the values. It will count the occurrences of each.

Partitioning SQL query by arbitrary number of rows

I have a SQL table with periodic measurements. I'd like to be able to return some summary method (say SUM) over the value column, for an arbitrary number of rows at a time. So if I had
id | reading
1 10
5 14
7 10
11 12
13 18
14 16
I could sum over 2 rows at a time, getting (24, 22, 34), or I could sum 3 rows at a time and get (34, 46), if that makes sense. Note that the ID might not be contiguous -- I just want to operate by row count, in sort order.
In the real world, the identifier is a timestamp, but I figure that (maybe after applying a unix_timestamp() call) anything that works for the simple case above should be applicable. If it matters, I'm trying to gracefully scale the number of results returned for a plot query -- maybe there's a smarter way to do this? I'd like the solution to be general, and not impose a particular storage mechanism/schema on the data.
You may resequense query result and then group it
SET #seq = 0;
SELECT SUM(data), ts FROM (
SELECT #seq := #seq + 1 AS seq, data, ts FROM table ORDER BY ts LIMIT 50
) AS tmp GROUP BY floor(tmp.seq / 3);