I am using Ruby on Rails 3.2.9 and MySQL. I have an Article model with a user_id attribute (this attribute represents the foreign key - id - for associated author users) and I would like to retrieve articles ordering those "for" a given author. That is, given I have following records:
<#Article id: 1, title: "Title 1", user_id: 1>
<#Article id: 2, title: "Title 2", user_id: 2>
<#Article id: 3, title: "Title 3", user_id: 1>
<#Article id: 4, title: "Title 4", user_id: 3>
<#Article id: 5, title: "Title 5", user_id: 1>
...
<#Article id: N, title: "Title N", user_id: M>
When I look for articles ordered "for" the author with id 1 (user_id = 1) then the returning articles should be ordered as-like the following:
<#Article id: 1, title: "Title 1", user_id: 1>
<#Article id: 3, title: "Title 3", user_id: 1>
<#Article id: 5, title: "Title 5", user_id: 1>
<#Article id: 2, title: "Title 2", user_id: 2>
<#Article id: 4, title: "Title 4", user_id: 3>
...
<#Article id: N, title: "Title N", user_id: M>
In other words, I am looking to retrieve all articles but making those ordered with this "priority": articles created by the given author returned first and then all other articles (that is, I would like to "push ahead" articles created by a given author).
How can I make that?
Note: I am looking for a Ruby on Rails implementation, maybe through the order method.
Your example is not that clear since you are searching for user_id 1 and a normal ordering by user_id would put those first anyway. I believe you mean to do something like:
SELECT id, title, user_id
FROM myTable
ORDER BY CASE WHEN user_id = #search_id THEN 1 ELSE 2 END, user_id
In your example above, #search_id should be 1.
Try:
Article.where(user_id: 1).order("user_id ASC").order("id ASC")
Or this:
Article.where(user_id: 1).order("user_id ASC, id ASC")
Try this ::
Select
*
from
myTable
order by
user_id, id
For a given user_id:
Select
*
from
myTable
where user_id='?'
order by
user_id, id
The query in Rails could be like this:
Article.order(Article.send(:sanitize_sql, ["IF(user_id = ?, 1, 2)", params[:user_id]]))
IF(user_id = ?, 1, 2) returns 1 or 2 for ordering articles. sanitize_sql, a private method, is used to sanitize parameters.
If params[:user_id] is always an integer, the query could be simplified to this:
Article.order("IF(user_id = #{params[:user_id].to_i}, 1, 2)")
Related
I want to fire a query to get such a result:
[{
id: 1,
brandName: "x"
brandModels: [
{id: 1, modelName: "abc", createdAt: "yyyy-mm-dd"},
{id: 2, modelName: "def", createdAt: "yyyy-mm-dd"},
]
},
{
id: 2,
brandName: "y"
brandModels: [
{id: 4, modelName: "ghi", createdAt: "yyyy-mm-dd"},
{id: 5, modelName: "jkl", createdAt: "yyyy-mm-dd"},
]
}]
Tables Schema
BrandsTable
{id, brandName, brand_id}
ModelsTable
{id, modelName, createdAt}
I guess it's not possible like that? I don't have any experience with text-based databases, but I can well imagine that this can be achieved with a MongoDB. Because ultimately I want to have a js object at the end of the day.
Here's an example but I have not tested it:
SELECT JSON_ARRAYAGG(
JSON_OBJECT(
'id', b.id,
'brandName', b.brandName,
'brandModels', m.modelArray
)
) AS joined_result
FROM BrandTable AS b
JOIN (
SELECT brand_id, JSON_ARRAYAGG(
JSON_OBJECT(
'id', id,
'modelName', modelName,
'createdAt', createdAt
)
) AS modelArray
FROM ModelsTable
GROUP BY brand_id
) AS m USING (brand_id);
Note that I had to assume the ModelsTable also contains a column for brand_id, otherwise there is nothing to use in the join condition.
Read documentation for JSON_OBJECT() and JSON_ARRAYAGG() for more information about how these functions work.
This question already has answers here:
How to Display row even if a record does not exist in another table?
(2 answers)
Closed 9 months ago.
So I got an admin dashboard which are not linked by ID but by name. So developer.name is the core name, dateName and absentName are meant to join this developer.name value.
What I got so far is this
db.query("SELECT id, name, absentday, date, slack_id, selected
FROM developers, absent, date
WHERE date.dateName=developers.name
AND absent.absentName=developers.name",
I certainly know this is wrong, anyone have suggestions on how I can make 2 tables join 1 table using only one value?
Developers=
id
DevelopersName
selected
Absent=
absent_id
absent_name
absentdays
Date=
date_id
date_name
date
I have an admin page, and on this page people are able to create an absence day for when they're not present. Which is why I have the table Absence. They only have to put in their name, so lets say Tom Hiddle, and the day they are absent. This will be inserted into the database, This is the same story for the Date table.
Now I have a BOT, Basically, the code and query I've written is meant to exclude the people who are absent on certain days, AND those who are on holidays. I am able to make the Absent table work, correctly excluding the absent people from the query. HOWEVER, I now meet the problem where I have to join DateName and AbsentName to DevelopersName value. Why don't I use IDs for this instance? It's simple, when records are deleted the ID selection will mess up, and since I'm creating this system for a company I work for It cannot have room for error, hence I use identical names to link tables between each other.
So In short, I don't use IDs for multiple reasons, Instead I use identical names between tables. DateName and AbsentName are meant to join with DevelopersName, If I join one of them I get results, If I join both I get none.
// I have a CRUD Dashboard where I can insert absentdays for Developers.
// So lets say Developer Tom Riddle is not present on monday, I'll put his absentday on 1
// Monday to Friday (1-5)
absent = [
{ id: 1, absentName: 'Tom Riddle', absentday: 1},
{ id: 2, absentName: 'Hank Some', absentday: 2},
{ id: 3, absentName: 'Family Man', absentday: 3}
]
// Date is not the same as Absent, Date is a long term deposit of holidays,
// lets say Hank Some Is going on vacation for 3 days!
date = [
{ id: 1, dateName: 'Tom Riddle', date: '2022-05-13'},
{ id: 2, dateName: 'Hank Some', date: '2022-07-14'},
{ id: 3, dateName: 'Hank Some', date: '2022-07-15'},
{ id: 4, dateName: 'Hank Some', date: '2022-07-16'},
{ id: 5, dateName: 'Family Man', date: '2022-06-15'}
]
// This is the core information of the developers. These are NOT the only columns, there are many more, but for sample data I only noted down the most important one's.
developers = [
{ id: 51, developersName: 'Tom Riddle'},
{ id: 52, developersName: 'Hank Some'},
{ id: 53, developersName: 'Family Man'}
]
// Say I run this query
SELECT id, developersName, absentName, absentday FROM developers, absent WHERE absent.absentName=developers.developersName;
// The output will be this
developers = [
{ id: 51, developersName: 'Tom Riddle', absentName: 'Tom Riddle', absentdays: 1},
{ id: 52, developersName: 'Hank Some', absentName: 'Hank Some', absentdays: 2},
{ id: 53, developersName: 'Family Man', absentName: 'Family Man', absentdays: 3}
]
// I now have ABSENT joined with DEVELOPERS.
// With MORE code deeper into the file I can exclude absent people from the query
// Let's say It's monday, It would then look like this.
developers = [
{ id: 52, developersName: 'Hank Some', absentdays: 2},
{ id: 53, developersName: 'Family Man', absentdays: 3}
]
// This WORKS, my issue at the moment is combining DATE and ABSENT on Developers
// My WANTED result is this
developers = [
{ id: 51, developersName: 'Tom Riddle', absentdays: 1, date: '2022-05-13'},
{ id: 52, developersName: 'Hank Some', absentdays: 2, date: '2022-07-14'},
{ id: 52, developersName: 'Hank Some', absentdays: 2, date: '2022-07-15'},
{ id: 52, developersName: 'Hank Some', absentdays: 2, date: '2022-07-16'},
{ id: 53, developersName: 'Family Man', absentdays: 3, date: '2022-06-15'}
]
// After researching myself, Using this query will give these results above ^^
SELECT id, developersName, absentName, absentday FROM developers, absent WHERE absent.absentName=developers.developersName AND date.dateName=developers.developersName;
// With my problem of joining tables being fixed, I run into a new issue.
// Whenever I execute the query above, it will only display people with a DATE
// Let's look at these sample data here.
absent = [
{ id: 1, absentName: 'Tom Riddle', absentday: 1},
{ id: 2, absentName: 'Hank Some', absentday: 2},
{ id: 3, absentName: 'Family Man', absentday: 3},
{ id: 4, absentName: 'Buddy Friend', absentday: 4}
]
// Hank Some no longer has records in these table since his holidays have passed
// Buddy Friend has no holidays at all.
date = [
{ id: 1, dateName: 'Tom Riddle', date: '2022-05-13'},
{ id: 2, dateName: 'Family Man', date: '2022-06-15'}
]
// If I execute the query
SELECT id, developersName, absentday FROM developers, absent WHERE absent.absentName=developers.developersName AND date.dateName=developers.developersName;
// MY results are this
developers = [
{ id: 51, developersName: 'Tom Riddle', absentdays: 1, date: '2022-05-13'},
{ id: 53, developersName: 'Family Man', absentdays: 3, date: '2022-06-15'},
]
// It doesn't display the people who have NO holidays.
// Buddy Friend and Hank Some are MISSING!
So to the question: I need help getting Buddy Friend and Hank Some back in my results. And another question, is there a cleaner way of joining these tables together? Instead of using WHERE, maybe inner joins?
You are using a join syntax that was already out-dated when MySQL was developed. It is weird seeing you using it. Maybe you mistakenly took a book from the 1980s to learn SQL? Better quit this. MySQL still supports this old syntax (as it is still allowed), but we are not using it any longer, because it is less readable than explicit joins, much more prone to errors and doesn't support outer joins.
This is what your query looks like in the syntax we have been using in the last three decades:
SELECT developers.id, developers.developersName, absent.absentday, date.date
FROM developers
INNER JOIN absent ON absent.absentName = developers.developersName
INNER JOIN date ON date.dateName = developers.developersName;
This joins the three tables and returns all matches. If you want to show developers even when they don't have an entry in the absent or date table, use outer joins instead of inner joins:
SELECT developers.id, developers.developersName, absent.absentday, date.date
FROM developers
LEFT OUTER JOIN absent ON absent.absentName = developers.developersName
LEFT OUTER JOIN date ON date.dateName = developers.developersName;
Be aware though that with this data model, names must never change. This may be true for login names, but not for natural names, where names change when people getting married for instance. A more typical database design would hence be:
CREATE TABLE developer
(
developer_id int not null auto_increment,
first_name varchar(100) not null,
last_name varchar(100) not null,
primary key (developer_id)
);
CREATE TABLE developer_absent
(
developer_id int not null,
day_num int not null,
primary key (developer_id, day_num),
foreign key (developer_id) referencing developer (developer_id)
);
CREATE TABLE developer_holiday
(
developer_id int not null,
holiday date not null,
primary key (developer_id, holiday),
foreign key (developer_id) referencing developer (developer_id)
);
The query would then become:
SELECT d.developer_id, d.name, da.day_num, dh.date
FROM developers d
LEFT OUTER JOIN developer_absent da ON da.developer_id = d.developer_id
LEFT OUTER JOIN developer_holiday dh ON dh.developer_id = d.developer_id;
But while this is syntactically correct, it doesn't make much sense semantically, because the absent days and the holidays are not closely related. You'd see that Hank is on holiday on 2022-07-14 and absent on Tuesdays and on holiday on 2022-07-15 and absent on Tuesdays and on holiday on 2022-07-16 and absent on Tuesdays.
A more typical query would be for instance:
-- Who is available on Friday, May 20, 2022
select *
from developer
where developer_id not in
(
select developer_id
from developer_absent
where day_num = (dayofweek(date '2022-05-20') + 5) % 7 + 1
)
and developer_id not in
(
select developer_id
from developer_holiday
where holiday = date '2022-05-20'
);
(There is a little math involved to match your MON-SUN = 1-7 with MySQL's SUN-SAT = 1-7.)
I am trying to query a table reviews from a MySQL database. Each row represents a review made on a product. The data for the relevant columns is stored like this:
{ product_id: 25, rating: 1, recommend: 'false' },
{ product_id: 25, rating: 4, recommend: 'true' },
{ product_id: 25, rating: 3, recommend: 'true' },
{ product_id: 25, rating: 2, recommend: 'false' },
{ product_id: 25, rating: 1, recommend: 'false'}
I need to get the total of each type of rating (eg. how many reviews gave it a ‘1’), and then count how many reviews have recommend ‘true’ and how many have recommend ‘false’. I would like the query to return data like this:
{ product_id: 25, rating: 1, total: 2, recommendTrue: 2, recommendFalse: 3},
{ product_id: 25, rating: 2, total: 1, recommendTrue: 2, recommendFalse: 3},
{ product_id: 25, rating: 3, total: 1, recommendTrue: 2, recommendFalse: 3},
{ product_id: 25, rating: 4, total: 1, recommendTrue: 2, recommendFalse: 3},
Technically I don’t need the recommendTrue and recommendFalse to be on every line, but because I am grouping by the type of rating, the redundancy is fine.
When I use this query (A):
'SELECT product_id, rating, COUNT(rating) as total'
' ' + 'FROM reviews'
' ' + 'WHERE reviews.product_id = ?'
' ' + 'GROUP BY rating'
' ' + 'LIMIT 50'
I get back part of the desired result:
{ product_id: 25, rating: 1, total: 1 },
{ product_id: 25, rating: 3, total: 1 },
{ product_id: 25, rating: 2, total: 3 },
{ product_id: 25, rating: 5, total: 1 },
{ product_id: 25, rating: 4, total: 2 }
I now need to count the total number of True and False recommends amongst all the reviews for product_id 25.
I am trying this query (B):
"SELECT r.product_id, COUNT(r.recommend='false') as F, COUNT(r.recommend='true') as T"
" " + "FROM reviews AS r"
" " + "WHERE r.product_id = ?"
" " + "GROUP BY r.rating"
" " + "LIMIT 50"
And getting this result:
{ product_id: 25, F: 1, T: 1, rating: 1, total: 1 },
{ product_id: 25, F: 1, T: 1, rating: 3, total: 1 },
{ product_id: 25, F: 3, T: 3, rating: 2, total: 3 },
{ product_id: 25, F: 1, T: 1, rating: 5, total: 1 },
{ product_id: 25, F: 2, T: 2, rating: 4, total: 2 }
The recommends need to be counted separately for True and False but should not be counted separately for rating. The way I am writing it, it is counting ALL the recommends (true and false) per rating.
You have two separate queries: one for the true/false and one for the ratings.
You can combibe the two with a join statement. The query in the join will calculate the true/false and then the result is combined with the ratings query:
select product_id, rating, count(*), t, f
from reviews
join (
select
sum(if(recommend='true', 1, 0)) as t,
sum(if(recommend='false', 1, 0)) as f
from reviews
where product_id=?
) as q
where product_id=?
group by product_id, rating, t, f
See db-fiddle.
If you're on MySQL 8+ that supports window functions, you can try with SUM() OVER () function for the base query, then make that as a subquery to do the COUNT() and grouping. Something like this:
SELECT product_id, rating,
COUNT(product_id) AS total,
recommendTrue,
recommendFalse
FROM
(SELECT product_id, rating,
SUM(recommend='true') OVER () AS recommendTrue,
SUM(recommend='false') OVER () AS recommendFalse
FROM reviews r
WHERE r.product_id = '25') A
GROUP BY product_id, rating, recommendTrue, recommendFalse
Demo fiddle
I'm trying to calculate the sum of some JSON values in PLpgSQL (Postgres v9.5.5) but am stuck on the logic.
For this data set:
{
clientOrderId: 'OR836374647',
status: 'PENDING',
clientId: '583b52ede4b1a3668ba0dfff',
sharerId: '583b249417329b5b737ad3ee',
buyerId: 'abcd12345678',
buyerEmail: 'test#test.com',
lineItems: [{
name: faker.commerce.productName(),
description: faker.commerce.department(),
category: 'test',
sku: faker.random.alphaNumeric(),
quantity: 3
price: 40
status: 'PENDING'
}, {
name: faker.commerce.productName(),
description: faker.commerce.department(),
category: 'test',
sku: faker.random.alphaNumeric(),
quantity: 2,
price: 30,
status: 'PENDING'
}
I am trying to get the subtotal of all the lineItems for each row (i.e. quantity * price for each line item, then the sum of these values for the row). So for the above example, the returned value should be 180.
I got this far, but this is returning the totals for all lineItems in the table, not grouped by row.
WITH line_items AS (SELECT jsonb_array_elements(line_items) as line_items FROM public.order),
line_item_totals AS (SELECT line_items->>'quantity' AS quantity, line_items->>'price' AS price FROM line_items)
SELECT (quantity::int * price::numeric) AS sub_total FROM line_item_totals;
I'm sure the fix is simple but I'm not sure how to do this with JSON fields.
Please always include Postgres version you are using. It also looks like your JSON is incorrect. Below is an example of how you can accomplish this with json type and valid json document.
with t(v) as ( VALUES
('{
"clientOrderId": "OR836374647",
"status": "PENDING",
"clientId": "583b52ede4b1a3668ba0dfff",
"sharerId": "583b249417329b5b737ad3ee",
"buyerId": "abcd12345678",
"buyerEmail": "test#test.com",
"lineItems": [{
"name": "name1",
"description": "desc1",
"category": "test",
"sku": "sku1",
"quantity": 3,
"price": 40,
"status": "PENDING"
},
{
"name": "name2",
"description": "desc2",
"category": "test",
"sku": "sku2",
"quantity": 2,
"price": 30,
"status": "PENDING"
}]
}'::JSON)
)
SELECT
v->>'clientOrderId' cId,
sum((item->>'price')::INTEGER * (item->>'quantity')::INTEGER) subtotal
FROM
t,
json_array_elements(v->'lineItems') item
GROUP BY cId;
Result:
cid | subtotal
-------------+----------
OR836374647 | 180
(1 row)
# engine.rb
has_many :pistons
#piston.rb
belongs_to :engine
Piston has a column, piston_count and, of course, engine_id
My database has the following 7 records
Engine.all
#=> [#<Engine id: 1>, #<Engine id: 2>, #<Engine id: 3>]
Piston.all
#=> [#<Piston id: 1, engine_id: 1, piston_count: 1>, #<Piston id: 2, engine_id: 1, piston_count: 2>, #<Piston id: 2, engine_id: 2, piston_count: 1>, #<Piston id: 2, engine_id: 3, piston_count: 2>]
I want to write a query that says, return the Engine containing Pistons with a piston_count of 1 and also contains a piston_count of 2
I've tried...
engines = Engine.joins(:pistons).merge(Piston.where(piston_count: 1))
#=> [#, #]
engines.joins(:pistons).merge(Piston.where(piston_count:2))
#=> []
It returns an empty array because active record turns that into one AND clause. However, if I do an OR statement, it will return too many records. Any thoughts?
Figured it out. This takes the intersect of both Active Record Queries.
engine_ids = Engine.joins(:pistons).merge(Piston.where(piston_count: 1)).pluck(:id) & Engine.joins(:pistons).merge(Piston.where(piston_count: 2)).pluck(:id)
Then go back and retrieve all the intersects.
Engine.where(id: engine_ids)