I have a sliders table. It is something that looks like this:
+----+-----------+-----------+
| id | video_url | image_url |
+----+-----------+-----------+
| 1 | null | imgurl1 |
+----+-----------+-----------+
| 2 | null | imgurl2 |
+----+-----------+-----------+
| 3 | null | imgurl3 |
+----+-----------+-----------+
| 4 | vidurl1 | null |
+----+-----------+-----------+
I can achieve what I want using this query:
(SELECT * FROM sliders WHERE image_url IS NOT NULL LIMIT 1)
UNION
(SELECT * FROM sliders WHERE video_url IS NOT NULL LIMIT 1)
UNION
(SELECT * FROM sliders)
Basically, the order I want is:
First Image
First Video
...
Everything else
So based on the example, the result should be (based on the id) is [1,4,2,3].
Is this possible to recreate without using UNION clause?
By the way, I am using Ruby on Rails on this project and currently using find_by_sql to execute the query. If you can help me use ActiveRecord instead, that would be great.
As of now, I can't see a way to union tables when using ActiveRecord.
Your query is no solution for the problem given. A query result is only then guaranteed to be sorted when you apply ORDER BY, which you don't. Your query boils down to a mere
SELECT * FROM sliders;
Even if you happen to get the rows in the desired order with your query now, this can be already different the next time you run it.
(Apart from this, you are applying LIMIT 1 without an ORDER BY clause, which just picks a record arbitrarily. You could get any of the image urls with the first subquery.)
You need an ORDER BY clause in which you must check whether the row's ID is the first image or the first video:
SELECT *
FROM sliders
ORDER BY
id = (SELECT MIN(id) FROM sliders WHERE image_url IS NOT NULL) DESC,
id = (SELECT MIN(id) FROM sliders WHERE video_url IS NOT NULL) DESC,
id;
(This makes use of MySQL's true = 1, false = 0. By sorting in descending order, we get true before false.)
One method in MySQL is to use variables:
select s.*
from (select s.*,
(case when image_url is not null then #rn_i := #rn_i + 1 end) as rn_i,
(case when video_url is not null then #rn_v := #rn_v + 1 end) as rn_v,
from sliders cross join
(select #rn_i := 0, #rn_v := 0) params
order by id
) s
order by (rn_i = 1) desc, (rn_v = 1) desc, id asc;
Related
I have a table. It has the following structure
goods_receiving_items
id
item_id
quantity
created_at
I am trying to fetch rows against which have the following conditions
Has one item_id
When the sum of the quantity column equals a certain value
So for example I have the following data
+----+---------+----------+------------+
| id | item_id | quantity | created_at |
+----+---------+----------+------------+
| 1 | 2 | 11 | 2019-10-10 |
| 2 | 3 | 110 | 2019-10-11 |
| 3 | 2 | 20 | 2019-11-09 |
| 4 | 2 | 5 | 2019-11-10 |
| 5 | 2 | 1 | 2019-11-11 |
+----+---------+----------+------------+
I have tried the following query:
SET #sum:= 0;
SELECT item_id, created_at, (#sum:= #sum + quantity) AS SUM, quantity
FROM goods_receiving_items
WHERE item_id = 2 AND #sum<= 6
ORDER BY created_at DESC
If I don't use ORDER BY, then the query will give me ID '1'. But if I use ORDER BY it will return all the rows with item_id = 2.
What should be returned are IDs '5' and '4' exclusively in this order
I can't seem to resolve this and ORDER BY is essential to my task.
Any help would be appreciated
You should use the order by on the resulting set
you could do this using a subquery
SET #sum:= 0;
select t.*
from t (
SELECT item_id
, created_at
, (#sum:= #sum + quantity) as sum
, quantity
FROM goods_receiving_items
WHERE item_id = 2 AND #sum<= 6
) t
ORDER BY created_at DESC
You should try an INNER JOIN with SELECT min(created_at) or SELECT max(created_at)
From MYSQL docs:
...the selection of values from each group cannot be influenced by
adding an ORDER BY clause. Sorting of the result set occurs after
values have been chosen, and ORDER BY does not affect which values the
server chooses.
The answers on the following might help in more detail: MYSQL GROUP BY and ORDER BY not working together as expected
After searching around, I have made up the following query
SELECT
t.id, t.quantity, t.created_at, t.sum
FROM
( SELECT
*,
#bal := #bal + quantity AS sum,
IF(#bal >= $search_number, #doneHere := #doneHere + 1 , #doneHere) AS whereToStop
FROM goods_receiving_items
CROSS JOIN (SELECT #bal := 0.0 , #doneHere := 0) var
WHERE item_id = $item_id
ORDER BY created_at DESC) AS t
WHERE t.whereToStop <= 1
ORDER BY t.created_at ASC
In the above query, $search_number is a variable that holds the value that has to be reached. $item_id is the item we are searching against.
This will return all rows for which the sum of the column quantity makes up the required sum. The sum will be made with rows in descending order by created_at and then will be rearranged in ascending order.
I was using this query to calculate the cost when a certain amount of items are being used in an inventory management system; so this might help someone else do the same. I took most of the query from another question here on StackOverflow
I have a table that contains id and country name, and I need to convert them so the id with more than 1 country will display in 1 row.I have been searching in this forum for over an hour and found nothing.
I tried if using the pivot function can help me to achieve the result i wanted, but I feel like using pivot does not work on my case here.
This is a mini version of the table I have. The number of distinct value in the field "country" will be over 100, so I can just say something like when county = '..' as this will be to repetitive.
enter code here
+----+--------+
| id | country|
+----+--------+
| 1 | US |
| 1 | UK |
| 2 | JP |
+----+--------+
Desired outcome I am looking for:
enter code here
+----+-----------+-----------+
| id | country_1 | country_2 |
+----+-----------+-----------+
| 1 | US | UK |
| 2 | JP | null |
+----+-----------+-----------+
I found this question which is similar but it is the opposite of what I am trying to achieve.
MySQL statement to pivot table without using pivot function or a union
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
update:
Thank you so much for all of your helps. I may not have used the queries of yours to solve my problem - as of the fact that the syntax is a bit diff running in snowflake. However, I got the insights I need from all of you.
here is my solution:
enter code here
select t1.id,
max(iff(t1.row_number = 1, t1.country ,null)) as country_1,
max(iff(t1.row_number = 2, t1.country ,null)) as country_2,
max(iff(t1.row_number = 3, t1.country, null)) as country_3
from
(
select id, country, row_number() over (partition by id order by id ) as
row_number
from table
) t1
group by t1.id
Whereas you could do it with "pivoting", what will happen when you have 3 countries? Or 4? Or 17?
May I suggest this:
SELECT id,
GROUP_CONCAT(country)
FROM tbl
GROUP BY id;
You will get something like:
1 US,UK
2 JP
use aggregation
select id, max(case when id=1 then country end ) as country_1,
max(case when id=2 then country end ) as country_2
from tbale group by id
As you comment on #Rick answer you have max 3 country for each id then you can use this
select
id,
(select country from test where test.id=t.id limit 0,1)as country_1,
(select country from test where test.id=t.id limit 1,1)as country_2,
(select country from test where test.id=t.id limit 2,1)as country_3
from test as t
group by id;
DEMO
You can try this following script with RowNumber generated per id. As you confirmed there are maximum 3 country per id, we can easily generate your desired result set by handling RowNumber 1,2 & 3
SELECT ID,
MAX(CASE WHEN RowNumber = 1 THEN country ELSE NULL END) Country_1,
MAX(CASE WHEN RowNumber = 2 THEN country ELSE NULL END) Country_2,
MAX(CASE WHEN RowNumber = 3 THEN country ELSE NULL END) Country_3
FROM
(
SELECT id,
country,
#row_num :=IF(#prev_value = concat_ws('',id),#row_num+1,1)AS RowNumber
,#prev_value := concat_ws('',id)
FROM tbale
ORDER BY id
)A
GROUP BY id
There's no "dynamic" PIVOT in SQL. You need to specify the list of columns when writing the query. Your options are:
If you know the number of columns in advance, then #ZaynulAbadinTuhin solution is the easier. It seems, however, this is not your case.
If you don't know the number of columns in advance and you want them all concatenated in a single column, then #Rick James solution is the best.
Otherwise, you can still use some kind of dynamic SQL in your app or in a stored procedure that will build the SQL query at runtime, based on the existing values of the table. But this solution would require much more programming. It's not a single/simple SQL query anymore. See Rick James's Pivoting in MySQL stored procedure.
I have the following MySQL query:
SELECT
#rownum:=#rownum+1 rank,
userID,
xpTotal
from users xpTotal, (SELECT #rownum:=0) r
WHERE username != '' && bot = 'false'
ORDER BY xpTotal DESC
Which results in something like this:
rank | userID | xpTotal
--------------------------------
1 | 2934729447 | 52873
2 | 8523954935 | 33465
3 | 4576456556 | 13466
4 | 2341234555 | 04244
5 | 3453565334 | 02297
How can I modify my query to get the rank of say ID 2341234555? Meaning, in this case, the query would only output the 4th row.
You need to wrap your current in a subquery, otherwise if you add it in the condition in the current WHERE clause, the rank would always be 1.
SELECT *
FROM
(
SELECT
#rownum:=#rownum+1 rank,
userID,
xpTotal
FROM users xpTotal, (SELECT #rownum:=0) r
WHERE username != '' && bot = 'false'
) a
WHERE a.UserID = '2341234555';
I'm trying to get the correct ORDER BY for my MySQL query, however I can't get the order by correct.
I have two columns:
breaking_news = values are NULL or 1
news_published_dt = values are DATETIME
How can I sort so that the first output are breaking_news NOT NULL sorted by DATETIME, and then the rest afterwards is just ordered by DATETIME?
I've tried this, but it gave me no output
ORDER BY CASE WHEN n.breaking_news IS NOT NULL THEN 1 ELSE 2 END, n.news_published_dt DESC
Looks like, you are looking for this:
SELECT
*
FROM
tableName
ORDER BY
breaking_news DESC,
news_published_dt ASC
SELECT * FROM table_name ORDER BY news_published_dt DESC
select * from
(select *, 1 Shortby from table 1 where (breaking_news is null)
union all
select *, 2 Shortby from table 1 where (breaking_news=1)
) a
order by Shortby ,news_published_dt Desc
SORT BY in MySQL works differently than what you (me included) would be expected. You cannot do a sort by of two columns and have each of the first sorted by columns sorted according to the second.
DOES NOT WORK:
ORDER BY breaking_news, news_published_dt
RESULT:
breaking_news | news_published_dt
1 | 2019-04-17
1 | 2019-04-04
null | 2019-05-01
null | 2019-05-06
Notice that the nulls are sorted correctly, however where breaking_news is 1; the results are not sorted according to what the poster wants.
The only way that I have found to accomplish this; query for the wanted boolean result individually and then union the two together.
THIS DOES WORK:
(SELECT * FROM news_table WHERE breaking_news = 1 ORDER BY news_published_dt)
UNION
(SELECT * FROM news_table WHERE breaking_news <> 1 ORDER BY news_published_dt)
RESULT:
breaking_news | news_published_dt
1 | 2019-04-04
1 | 2019-04-17
null | 2019-05-01
null | 2019-05-06
For a more thorough explanation of this, check out Edwin Wang's explanation
USE This
ORDER BY CASE WHEN n.breaking_news IS NOT NULL THEN n.breaking_news ELSE 'zzzzzzzzzzzzzzzzzzzzzz' END, n.news_published_dt DESC
So I have this bit of mysql that I'm trying to work out. My goal is to insert the count of a grouping into the primary records to tell me how many of each status is within the related table for the record, so the result might look like this:
| id | name | count1 | count2 |
------------------------------------
| 1 | primary 1 | 5 | 3 |
| 1 | primary 2 | 2 | 7 |
select * from primaryTable
left join (
select
case
when relationTable.relation_status_id = 1
then count(*)
END as count1,
case
when relationTable.relation_status_id = 2
then count(*)
END as count2
) relationTable
on relationTable.primary_id = primaryTable.id
I tried using a subquery to do it, which worked, but requires a select per count, which I'm trying to avoid.
Adding a group by to the subquery resulted in an error that more than one row was being returned.
In the subquery, rather than aggregate COUNT()s inside CASE, you may more easily use SUM() to add up the result of a boolean comparison (0 or 1) to return a result resembling a count.
SELECT
primaryTable.*,
count1,
count2
FROM
primaryTable
JOIN (
SELECT
primary_id,
-- Sum the results of a boolean comparison
SUM(relation_status_id = 1) AS count1,
SUM(relation_status_id = 2) AS count2
FROM relationTable
-- Group in the subquery
GROUP BY primary_id
-- Join the subquery to the main table by primary_id
) counts ON primaryTable.primary_id = counts.primary_id
Note that because MySQL treats the booleans the same as 0 or 1, the comparison relation_status_id = 1 returns 1 or 0. The syntax above isn't supported in every RDBMS. To be more portable, you would need to use a CASE inside SUM() to explicitly return an integer 1 or 0.
SUM(CASE WHEN relation_status_id = 1 THEN 1 ELSE 0 END) AS count1,
SUM(CASE WHEN relation_status_id = 2 THEN 1 ELSE 0 END) AS count2
Your original attempt has some syntax problems. Chiefly, it has no FROM clause, which is causing MySQL to think it should be treated as a scalar value and then complain that it returns more than one row.