Insert empty row after group - mysql

I have a table that contains transaction data. The rows with the same 'group_id' are a part of the same transaction. I am running the following SQL query to show all the transactions:
SELECT * FROM transactions
When I run this query I get as expected a list of all the transactions. But this large list makes it difficult to seperate the data with a different group_id from the other data.
For that reason I want to add an empty row at the end of the group_id, so I get:
1
1
(empty row)
2
2
2
instead of:
1
1
2
2
2
Can someone help me with this?
Here is my database:
http://sqlfiddle.com/#!9/b9bf79/1

I do not suggest you do this at all but if you just want to separate two groups you could do this:
SELECT * FROM transactions WHERE group_id = 1
UNION ALL
(SELECT '','','','','','')
UNION ALL
SELECT * FROM transactions WHERE group_id = 2
Obviously this can added to if there are more group ids in the future but it is not a general purpose solution you are really better off dealing with appearance issues like this in application code.

you can use (abuse) rollup.
SELECT *
FROM transactions
group by group_id, id
with rollup
having group_id is not null
this will insert a row with id set to null after each group_id.
mysql will also sort by group_id because of the group by.
The group by id` makes sure that all rows are shown (your schema does not show it, but I assume id is unique? Otherwise you need to add other fields)
However only id will be null in the extra rows. The other columns repeat the value above.
You can filter them like this:
SELECT
id,
case id is not null when true then date else null end as date,
case id is not null when true then group_id else null end as group_id
-- ....
FROM transactions
group by group_id, id
with rollup
having group_id is not null
Alternatively:
select * from
(SELECT *
FROM transactions
union all
select distinct null, null, group_id, null, null,null from transactions
) as t
order by 3,1
but null values are sorted first, so the "gap" is in front of each section

Related

Make a select with max and min passing condition to each of the two

When a post is accessed, I need, in addition to returning the information of this posts, to return the previous one if it exists and the next one.
I would like to know if there is a way to select MAX(id) and MIN(id) in a single query/select, passing a condition for each one of them. Example of what I'm trying to do in Laravel and I'll write it in SQL to make it easier too
Laravel:
$query = Post::query();
$query = $query->from('posts')->select(DB::raw('MAX(id), MIN(id)'))->whereRaw("id < {$id} and id > {$id}")->first();
SQL:
select MAX(id), MIN(id) from `posts` where id < 5 and id > 5 limit 1
The id variable is the post id value. In this example, it has the value 5. The query I'm doing is to get the MAX and MIN referring to this id, but I also need to get the info of the post that the user accessed.
The DB has post id number 4 and number 6. That is, I need to get the information from posts number 4, 5 and 6 in this example.
The where condition will never be true, but I cannot use or. The first condition is for MAX and the second for MIN. If I use the or, the biggest id will come of the DB.
I need to get the min and max value compared to a value. That is, as I explained above. If the id is 5, I need to get the largest existing id() below that value and I need to get the smallest value above it. In my case, from the information I have in the DB, it would be id 4, 5 and 6
Is it possible in a single consultation or do I really have to do more than one?
Yes, you can do it with case-when
select MAX(
CASE
WHEN id < 5 THEN id
ELSE NULL
END
), MIN(
CASE
WHEN id > 5 THEN id
ELSE NULL
END
)
from `posts`
where id <> 5
EDIT
Laravel equivalent, as shared by Gabriel Edu in the comment-section:
$query = Post::query();
$query = $query->from('posts')->
select(DB::raw("MAX(CASE WHEN id < {$id} THEN id ELSE null END), MIN(CASE WHEN id > {$id} THEN id ELSE null END)"))->first();
The LEAD() and LAG() function in MySQL are used to get preceding and succeeding value of any row within its partition.
Try this:
SELECT ID,
LAG (id) OVER (ORDER BY NULL) ONE_SHIFT_FORWARD,
LEAD (id) OVER (ORDER BY NULL) ONE_SHIFT_BACKWARD
FROM POSTS
ORDER BY ID ASC;
SELECT *
FROM ( SELECT ID,
LAG (id) OVER (ORDER BY NULL) ONE_SHIFT_FORWARD,
LEAD (id) OVER (ORDER BY NULL) ONE_SHIFT_BACKWARD
FROM POSTS
ORDER BY ID ASC)
WHERE id = 5;
You may use lead and lag to access the values before and after the current row.
You may then use those to select the post with a given id and the values before and after in a single select.
The following query
select *
from (
select
p.*,
lead(id) over(order by id) _lead,
lag(id) over(order by id) _lag
from post p
) x
where 23 in (id, _lead, _lag);
results in
id
text
_lead
_lag
15
fifteen
23
10
23
twentythree
24
15
24
twentyfour
50
23
With the following setup:
Schema (MySQL v8.0)
create table post (
id integer,
text varchar(50)
);
insert into post(id, text)
values
( 10, 'ten'),
( 15, 'fifteen'),
( 23, 'twentythree'),
( 24, 'twentyfour'),
( 50, 'fifty');
View on DB Fiddle

Count users and groups totals in one query

I am super new to access and having difficulty with a query. My table (tblpacks) is the following:
id user_id group_id quota_id
1 1 1 1
2 1 2 1
3 2 1 1
4 3 1 1
Ideally, what I now is to get hte number of unique users and groups for quota_id=1
The result will be:
total_users = 3
total_groups = 2
If you only wanted to count one field, there would by a simple solution, but since you want to count 2 separate fields, you in fact need at least 2 separate queries.
My answer is to use a UNION query as the source for counting. This UNION query returns the distinct user_id values (with Null as group_id) and the distinct group_id values (with Null as user_id). I omitted the DISTINCT keyword, because UNION (without ALL) does a DISTINCT query automatically. As the datatypes where not recognized correctly when using a constant Null field in the first SELECT statement of the UNION query, I added a third SELECT statement as the first one, which selects both fields from the table but returns no records:
SELECT Count(user_id) AS total_users, Count(group_id) AS total_groups
FROM (
SELECT user_id, group_id FROM tblpacks WHERE Yes=No
UNION
SELECT user_id, Null FROM tblpacks WHERE quota_id=1
UNION
SELECT Null, group_id FROM tblpacks WHERE quota_id=1
) AS qsub;

mysql select rows with same ids and preserve their order?

just a quick question:
i have to have one single query that has multiple rows - some rows are identicle - and the order of rows must be preserved in the result -
some idea of what im refering to:
SELECT id,date
FROM items
WHERE id IN (1,2,1,3)
ORDER BY id=1 DESC,id=2 DESC,id=1 DESC,id=3 DESC;
unfortunately mysql result is this:
1,2,3
not 1,2,1,3
it removes the duplicate which i have to have in my result to display in multiple panels on the same webpage -
i really dont want to loop thru each id one by one to get them the way i want to display -
is there a way to actually have one single query that will preserve the order and pull out rows based on request whether its unique or not -
Your query as it stands will never work, because duplicate values in a list of values of an IN clause are ignored. The only way to make this work is by using UNION ALL:
SELECT id, date FROM items where id = 1
UNION ALL
SELECT id, date FROM items where id = 2
UNION ALL
SELECT id, date FROM items where id = 1
UNION ALL
SELECT id, date FROM items where id = 3;
But to be frank, I suspect your data model so far past screwed it's unusable.
try
SELECT
id,
date
FROM items
WHERE id IN (1,2,1,3)
ORDER BY FIND_IN_SET(id, '1,2,1,3')
Another scrupulous way to answer a suspicious question:
SELECT
items.id,
items.date
FROM
items
JOIN
( SELECT 1 AS id, 1 AS ordering
UNION ALL
SELECT 2, 2
UNION ALL
SELECT 1, 3
UNION ALL
SELECT 3, 4
) AS auxilary
ON
auxilary.id = items.id
ORDER BY
auxilary.ordering
Another approach (untested, but should give you the idea):
CREATE TEMPORARY TABLE tt (id INT, ai int unsigned auto_increment primary key);
INSERT INTO tt (id) VALUES (1), (2), (1), (3);
SELECT
id,
date
FROM items JOIN tt USING (id)
ORDER BY tt.ai;
keeps the given order.
If you want to include the records with id=1 and the order doesn't matter as long as you get them, you can split your query into two queries, one for (1,2,3) union all the other query for id=1 or just do:
... In (1,2)
Union all
... In (1,3)
Example:
Select * from
(Select case id when 1 then 1 when 2 then 2 as pseudocol, othercolumns
From table where Id in (1,2)
Union all
Select case id when 1 then 3 when 3 then 4 as pseudocol, othercolumns
From table where Id in (1,3)) t order by pseudocol
Instead of doing what you are trying to, just select the unique rows you need. In the frontend code, store each unique row once in a key=>value structure, where key is the item ID and value is whatever data you need about that item.
Once you have that you can use frontend logic to output them in the desired order including duplicates. This will reduce the amount of redundant data you are trying to select.
For example This is not usable code - exact syntax required depends on your scripting language
-- setup a display order
displayOrder= [1,2,1,3];
-- select data from database, order doesn't matter here
SELECT id,date
FROM items
WHERE id IN (displayOrder);
-- cache the results in a key=> value array
arrCachedRows = {};
for (.... each db row returned ...) {
arrCachedRows[id] = date;
}
-- Now output in desired order
for (listIndex in displayOrder) {
-- Make sure the index is cached
if (listIndex exists in arrCachedRow) {
echo arrCachedRows[listIndex ];
}
}
If you must persist in using UNION despite my warnings
If you go against the above recommendation and absolutely MUST have them back in 1 query in that order then add on an additional row which will enforce the row order. See below query where I use variable #subIndex to add an incrementing value as subIndex. This in turn lets you reorder by that and it'll be in the requested order.
SELECT
i.*
FROM (
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 1
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 2
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 1
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 3
) AS i,(SELECT #subIndex:=0) v
ORDER BY i.subIndex
Or a slightly cleaner version that keeps item selection until the outside and hides the subindex
SELECT
items.*
FROM items
-- initialise variable
INNER JOIN (SELECT #subIndex:=0) v
-- create a meta-table with the ids desired in the order desired
INNER JOIN (
SELECT #subIndex:=#subIndex+1 AS subIndex, 1 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 2 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 1 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 3 AS id
) AS i
ON i.id = items.id
-- order by the subindex from i
ORDER BY i.`subIndex` ASC

UNION and LIMIT 1 clarification

I have a terrible doubt.
I have a query like this:
SELECT id,fatherID FROM comments WHERE fatherID IS NULL
UNION
SELECT id,fatherID FROM comments WHERE fatherID IS NOT NULL
LIMIT 1
(Note the limit 1 applies to the union, not to only the second query)
(Note2: IS NULL, and IS NOT NULL are just an example, they can be a random string too)
With this limit 1 I am sure that this kind of query will return the row from the first query all the time?
Or even if both query returns something it can happen that the LIMIT 1 get the row coming from the second query?
I am asking this because If i run that query (wtihtou limit 1) I get this results
http://img856.imageshack.us/img856/5212/immaginejv.jpg
When I would except something like this (First all the rows with fatherid = null, then the other):
http://img847.imageshack.us/img847/479/immaginehx.jpg
Order of returned results is (by default) undefined. If you want them ordered in a specific manner, use ORDER BY, it was made to do exactly this.
I have a question regarding your query. Why you would require this query? You are nothing but selecting all rows by making union of both opposite queries i.e
fatherID IS NULL OR NOT NULL
How can duplicate rows will present in result by this union query?
SELECT id, fatherID
FROM comments
WHERE fatherID IS NULL
UNION
SELECT id, fatherID
FROM comments
WHERE fatherID IS NOT NULL
LIMIT 1
You can get result (First all the rows with fatherid = null, then the other): directly by this query:
SELECT id, fatherID
FROM comments
ORDER By fatherID
Updated answer:
SELECT *
FROM
(SELECT
id, fatherID
FROM
comments
WHERE
fatherID = 'somerandomstring1'
UNION
SELECT
id, fatherID
FROM
comments
WHERE
fatherID = 'somerandomstring2') combined_comments
ORDER BY
combined_comments.fatherID
LIMIT 1

Get most recent row for given ID

In the table below, how do I get just the most recent row with id=1 based on the signin column, and not all 3 rows?
+----+---------------------+---------+
| id | signin | signout |
+----+---------------------+---------+
| 1 | 2011-12-12 09:27:24 | NULL |
| 1 | 2011-12-13 09:27:31 | NULL |
| 1 | 2011-12-14 09:27:34 | NULL |
| 2 | 2011-12-14 09:28:21 | NULL |
+----+---------------------+---------+
Use the aggregate MAX(signin) grouped by id. This will list the most recent signin for each id.
SELECT
id,
MAX(signin) AS most_recent_signin
FROM tbl
GROUP BY id
To get the whole single record, perform an INNER JOIN against a subquery which returns only the MAX(signin) per id.
SELECT
tbl.id,
signin,
signout
FROM tbl
INNER JOIN (
SELECT id, MAX(signin) AS maxsign FROM tbl GROUP BY id
) ms ON tbl.id = ms.id AND signin = maxsign
WHERE tbl.id=1
SELECT *
FROM tbl
WHERE id = 1
ORDER BY signin DESC
LIMIT 1;
The obvious index would be on (id), or a multicolumn index on (id, signin DESC).
Conveniently for the case, MySQL sorts NULL values last in descending order. That's what you typically want if there can be NULL values: the row with the latest not-null signin.
To get NULL values first:
ORDER BY signin IS NOT NULL, signin DESC
You may want to append more expressions to ORDER BY to get a deterministic pick from (potentially) multiple rows with NULL.
The same applies without NULL if signin is not defined UNIQUE.
Related:
mysql order by, null first, and DESC after
The SQL standard does not explicitly define a default sort order for NULL values. The behavior varies quite a bit across different RDBMS. See:
https://docs.mendix.com/refguide/null-ordering-behavior
But there are the NULLS FIRST / NULLS LAST clauses defined in the SQL standard and supported by most major RDBMS, but not by MySQL. See:
SQL how to make null values come last when sorting ascending
Sort by column ASC, but NULL values first?
Building on #xQbert's answer's, you can avoid the subquery AND make it generic enough to filter by any ID
SELECT id, signin, signout
FROM dTable
INNER JOIN(
SELECT id, MAX(signin) AS signin
FROM dTable
GROUP BY id
) AS t1 USING(id, signin)
Select [insert your fields here]
from tablename
where signin = (select max(signin) from tablename where ID = 1)
SELECT * FROM (SELECT * FROM tb1 ORDER BY signin DESC) GROUP BY id;
I had a similar problem. I needed to get the last version of page content translation, in other words - to get that specific record which has highest number in version column. So I select all records ordered by version and then take the first row from result (by using LIMIT clause).
SELECT *
FROM `page_contents_translations`
ORDER BY version DESC
LIMIT 1
Simple Way To Achieve
I know it's an old question
You can also do something like
SELECT * FROM Table WHERE id=1 ORDER BY signin DESC
In above, query the first record will be the most recent record.
For only one record you can use something like
SELECT top(1) * FROM Table WHERE id=1 ORDER BY signin DESC
Above query will only return one latest record.
Cheers!