| script | Db Slim Select Query | select cust_name from customer where cust_id = 70903 |
| check | data; | cust_name | 0 | JAMES SMITH |
The one above works but the one below doesn't.
|Query:Db Slim Select Query | select cust_id, cust_name from customer where cust_id = 70903 |
| cust_id | cust_name |
| 70903 | JAMES SMITH |
The message I got was
Query:Db Slim Select Query select cust_id, cust_name from customer where cust_id = 70903
cust_id cust_name
[70903] missing JAMES SMITH
field cust_id not present field cust_name not present
Did I missing something obilvous? according to the markfink's example this should not have failed.
Try to use a simple Query table:
| query | select cust_id, cust_name from customer where cust_id = 70903 |
| cust_id | cust_name |
| 70903 | JAMES SMITH |
If this doesn't help try to follow the query examples on this page: https://dbfit.github.io/dbfit/docs/reference.html#query
Related
I have one table with 3 columns are below
+---------------------------------------+
| id | name | parent_id |
+---------------------------------------+
| -1 | / | |
| 1 | Organization | -1 |
| 2 | United States | 1 |
| 3 | Business Analyst | 1 |
| 4 | Human Resources | 1 |
| 5 | Benefits Manager | 4 |
| 6 | Metropolitan Plant | 2 |
| 7 | Administration | 6 |
+---------------------------------------+
And my query is like this
SELECT CONCAT(parent.name, '/', child.name) AS path
FROM table_name AS child INNER JOIN table_name AS parent
ON child.id = parent.parent_id
I am expecting output as below.
/Organization
/Organization/United States
/Organization/Business Analyst
/Organization/Human Resources
/Organization/Human Resources/Benefits Manager
/Organization/United States/Metropolitan Plant
/Organization/United States/Metropolitan Plant/Administration
Ok...there might be a more elegant way to do this...especially with using do loops...but with what immediately comes to mind, you may need to do several joins. Is the maximum level low? I hope so. Here's an idea, but it's messy and may require a lot of spool depending on your data size:
SELECT CONCAT(path2, '/', D.name) AS path3
FROM
(SELECT CONCAT(path1, '/', B.name) AS path2
FROM
(SELECT CONCAT(parent.name, '/', child.name) AS path1
FROM table_name AS parent LEFT JOIN table_name AS child
ON child.id = parent.parent_id) AS A
LEFT JOIN TABLE_NAME AS B
ON A.id = B.parent_id) AS C
LEFT JOIN TABLE_NAME AS D
ON C.id = D.parent_id
The above code would only take it up to 3 levels. If something better comes to mind, I'll post it.
Suspect you're expected to use a hierarchical query here
WITH foo (id, parent_id, name, fullpath)
AS (SELECT id,
parent_id,
name,
'/' AS fullpath
FROM table_name
WHERE parent_id IS NULL
UNION ALL
SELECT m.id,
m.parent_id,
m.name,
f.fullpath || m.name || '/' AS fullpath
FROM foo f JOIN table_name m ON (m.parent_id = f.id))
SELECT fullpath FROM foo
WHERE id > 0
That'll be pretty close.
i am new to ROR.
i am building a classified ads app, i have the following tables in my database:
(some fields have been removed for simplicity)
Table Uers
This table stores all the users.
user_id
name
email
password
Table Ads
This table stores all the ads.
ad_id
users_user_id (FK)
title
desc
cat_id (FK)
created_at
Sample data:
------------------------------------------------------------------------------
| ad_id | users_user_id | title | desc | cat_id | created_at |
------------------------------------------------------------------------------
| 1 | 1 | iphone 4 | brand new | 2 | 30-11-2015 |
------------------------------------------------------------------------------
Table categories
This table stores all the available categories. cat_id in the ads table relates to cat_id in this table.
cat_id
category
parent_cid
Sample data:
-------------------------------------------
|cat_id| category | parent_cid |
-------------------------------------------
|1 | Electronics | NULL |
|2 | Mobile Phone | 1 |
|3 | Apartments | NULL |
|4 | Apartments - Sale | 3 |
-------------------------------------------
Table ads_attribute
This table contains all the available attributes for a particular category. Relates to categories table.
attr_id
cat_id (FK)
attr_label
attr_name
Sample data:
-----------------------------------------------------------
|attr_id | cat_id | attr_label | attr_name |
-----------------------------------------------------------
|1 | 2 | Operating System | Operating_System |
|2 | 2 | Is Touch Screen | Touch_Screen |
|3 | 2 | Manufacturer | Manufacturer |
|4 | 3 | Bedrooms | Bedrooms |
|5 | 3 | Total Area | Area |
|6 | 3 | Posted By | Posted_By |
-----------------------------------------------------------
Table ads_attr_value
This table stores the attribute value for each ad in ads table.
attr_val_id
attr_id (FK)
ad_id
attr_val
Sample data:
---------------------------------------------
|attr_val_id | attr_id | ad_id | attr_val |
---------------------------------------------
|1 | 1 | 1 | Ios 8 |
|2 | 2 | 1 | 1 |
|3 | 3 | 1 | Apple |
---------------------------------------------
What is the best way (the rails way) to validate the data before storing it in the the ads_attr_value table, given the fact that the values would be in select fields and the user can change them easily for example from Ios 8 to "blabla".
I've thought of storing all the possible values for each attribute in a new table and then check if a value sent by the user exist in that table before storing it in the ads_attr_value. what do you think? I am sure that there is a better way.thanks for sharing.
The rails way would probably to define your relationships with ActiveRecord associations : http://guides.rubyonrails.org/association_basics.html.
Therefore you could easily define on your model
class AdsAttrVal < ActiveRecord::Base
belongs_to :ad
validates :ad, presence: true
end
However please keep in mind that rails way to store an id of the table is to name it "id" and not "model_id" like you did ("user_id", "id"). My exemple suppose that the rails way is respected...
You have to specify the validations you want inside <yourModel>.rb (the model file) . For exame if you want to validate if ad_id is a number you should add the numericality parameter in the validates statement, see below:
class AdsAttrValue < ActiveRecord::Base
validates :ad_id, numericality: true
#validate if add_att_value has the permitted values
validate :myCustomValidation
def myCustomValidation
#your logic of validation goes here
#you can access here all the fields from this object recently created
if attr_val == something
#do something
end
end
end
See that validations from rails have an s at the end (validates), and your own written validations do not have (validate).
This validations are executed when creating the object before storing in database in order to see if it complies the validations and not stored it it does not comply. You can add errors in your own validation to let the user know what gone wrong. Go further with this reading of validations in ruby on rails
I'm having a little trouble trying to get a query to work the way I want it, I'm not getting all the results I'm hoping for.
I have 3 models Post, Comment and Tag. Both the posts and the comments can contain tags, and both have a has_and_belongs_to_many relationship with tags. I want to be able to get all the posts that either have a specified tag or have comments with that tag, I've been doing it in the following scope on posts like so:
scope :tag, -> (tag_id) { joins(:tags, :comment_tags).where("tags_posts.tag_id = :tag_id OR comments_tags.tag_id = :tag_id", tag_id: tag_id) }
But that doesn't return all the posts, just a subset of them, seems like its only the ones regarding the comments, this is the query it generates:
SELECT COUNT(*) FROM "posts"
INNER JOIN "tags_posts" ON "tags_posts"."post_id" = "posts"."id"
INNER JOIN "tags" ON "tags"."id" = "tags_posts"."tag_id"
INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
INNER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id"
INNER JOIN "tags" "comment_tags_posts" ON "comment_tags_posts"."id" = "comments_tags"."tag_id"
WHERE (tags_posts.tag_id = 1 OR comments_tags.tag_id = 1)
These are the models:
class Post < ActiveRecord::Base
has_and_belongs_to_many :tags
has_many :comment_tags, through: :comments, source: :tags
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :posts
has_and_belongs_to_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
has_and_belongs_to_many :tags
end
I'm not certain whether you've already figured this out, but in case you haven't, here is a possible solution:
In plain SQL, mainly for illustration purposes:
SELECT
DISTINCT posts.*
FROM
posts
INNER JOIN
tags_posts ON tags_posts.post_id = posts.id
LEFT JOIN
comments ON comments.post_id = posts.id
LEFT JOIN
comments_tags ON comments_tags.comment_id = comments.id
INNER JOIN
tags ON (tags.id = tags_posts.tag_id OR tags.id = comments_tags.tag_id)
WHERE tags.id = 1
The primary issue in your original version was that you were making an INNER JOIN with comments and comments_tags. As a result you were probably cutting out every Post which did not have any comments. So the solution is to LEFT JOIN everything related to the comments. And then, because we are left joining, we can INNER JOIN tags on either the tag posts or comment posts.
Converting to Active Record is not very pretty, but necessary:
Post.joins("INNER JOIN posts_tags ON posts_tags.post_id = posts.id")
.joins("LEFT JOIN comments ON comments.post_id = posts.id")
.joins("LEFT JOIN comments_tags ON comments_tags.comment_id = comments.id")
.joins("INNER JOIN tags ON (posts_tags.tag_id = tags.id OR comments_tags.tag_id = tags.id)")
.where(tags: {id: 1})
.uniq
Note the necessity of DISTINCT and uniq, as you will get duplicates because of the LEFT JOIN.
Edit
In case there's some misunderstanding of the dataset or structure, this is an example of the data I used in my test to create the above query.
posts
+----+--------------------------+
| id | text |
+----+--------------------------+
| 1 | Post about programming 1 |
| 2 | Post about programming 2 |
| 3 | Post about programming 3 |
| 4 | Post about cooking 1 |
| 5 | Post about cooking 2 |
+----+--------------------------+
tags
+----+-------------+
| id | name |
+----+-------------+
| 1 | programming |
| 2 | cooking |
| 3 | woodworking |
+----+-------------+
tags_posts
+--------+---------+
| tag_id | post_id |
+--------+---------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 4 |
| 2 | 5 |
+--------+---------+
comments
+----+----------------------------------------------+---------+
| id | comment_text | post_id |
+----+----------------------------------------------+---------+
| 1 | comment - programming on programming post 1a | 1 |
| 2 | comment - programming on programming post 1b | 1 |
| 3 | comment - programming on programming post 2a | 2 |
| 4 | comment - cooking on programming post 3a | 3 |
| 5 | comment - programming on cooking post 4a | 4 |
| 6 | comment - cooking on cooking post 4b | 4 |
| 7 | comment - cooking on cooking post 5a | 5 |
+----+----------------------------------------------+---------+
comments_tags
+--------+------------+
| tag_id | comment_id |
+--------+------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 5 |
| 2 | 4 |
| 2 | 6 |
| 2 | 7 |
+--------+------------+
If I want to search for "programming", the above query will yield this result set:
+----+--------------------------+
| id | text |
+----+--------------------------+
| 1 | Post about programming 1 |
| 2 | Post about programming 2 |
| 4 | Post about cooking 1 |
| 3 | Post about programming 3 |
+----+--------------------------+
since we have 3 posts specifically tagged with "programming", and one comment tagged as "programming" on a differently tagged post.
I am not sure to understand what's a yum, is it a post ?
From your SQL query it seems it will count only the yum that have both a specific tag AND comment with this specific tag. What you want is to count yum that have a specific tag OR comments with this specific tag.
I would do either 2 queries one to count the yum with specific tag + one to count the yum with specific commented tags and add them both to get the total or make one query with an UNION condition.
scope :yums_tagged, -> (tag_id) { joins(:tags).where("tags_yums.tag_id = :tag_id", tag_id: tag_id) }
scope :comments_taged, -> (tag_id) { joins(:comment_tags).where("comments_tags.tag_id = :tag_id", tag_id: tag_id) }
I'm trying to create an inbox for messaging between users.
Here are the following tables:
Messsages
Id | Message_from | message_to | message
1 | 2 | 1 | Hi
2 | 2 | 1 | How are you
3 | 1 | 3 | Hola
4 | 4 | 1 | Whats up
5 | 1 | 4 | Just Chilling
6 | 5 | 1 | Bonjour
Users
Id | Name
1 | Paul
2 | John
3 | Tim
4 | Rob
5 | Sarah
6 | Jeff
I'd like to display an inbox showing the list of users that the person has communicated and the last_message from either users
Paul's Inbox:
Name | user_id | last_message
Sarah| 5 | bonjour
Rob | 4 | Just Chilling
Tim | 3 | Hola
John | 2 | How are you
How do I do this with Active Records?
This should be rather efficient:
SELECT u.name, sub.*
FROM (
SELECT DISTINCT ON (1)
m.message_from AS user_id
, m.message AS last_message
FROM users u
JOIN messages m ON m.message_to = u.id
WHERE u.name = 'Paul' -- must be unique
ORDER BY 1, m.id DESC
) sub
JOIN users u ON sub.user_id = u.id;
Compute all users with the latest message in the subquery sub using DISTINCT ON. Then join to
table users a second time to resolve the name.
Details for DISTINCT ON:
Select first row in each GROUP BY group?
Aside: Using "id" and "name" as column names is not a very helpful naming convention.
How about this:
#received_messages = current_user.messages_to.order(created_at: :desc).uniq
If you want to include messages from the user as well, you might have to do a union query, or two queries, then merge and join them. I'm just guessing with some pseudocode, here, but this should set you on your way.
received_messages = current_user.messages_to
sent_messages = current_user.messages_from
(received_messages + sent_messages).sort_by { |message| message[:created_at] }.reverse
This type of logic is belongs to a model, not the controller, so perhaps you can add this to the message model.
scope :ids_of_latest_per_user, -> { pluck('MAX(id)').group(:user_id) }
scope :latest_per_user, -> { where(:id => Message.latest_by_user) }
Message.latest_per_user
I don't understand how to get the columns I want from rails. I have two models - A User and a Profile. A User :has_many Profile (because users can revert back to an earlier version of their profile):
> DESCRIBE users;
+----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(255) | NO | UNI | NULL | |
| password | varchar(255) | NO | | NULL | |
| last_login | datetime | YES | | NULL | |
+----------------+--------------+------+-----+---------+----------------+
> DESCRIBE profiles;
+----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | NO | MUL | NULL | |
| first_name | varchar(255) | NO | | NULL | |
| last_name | varchar(255) | NO | | NULL | |
| . . . . . . |
| . . . . . . |
| . . . . . . |
+----------------+--------------+------+-----+---------+----------------+
In SQL, I can run the query:
> SELECT * FROM profiles JOIN users ON profiles.user_id = users.id LIMIT 1;
+----+-----------+----------+---------------------+---------+---------------+-----+
| id | username | password | last_login | user_id | first_name | ... |
+----+-----------+----------+---------------------+---------+---------------+-----+
| 1 | john | ****** | 2010-12-30 18:04:28 | 1 | John | ... |
+----+-----------+----------+---------------------+---------+---------------+-----+
See how I get all the columns for BOTH tables JOINED together? However, when I run this same query in Rails, I don't get all the columns I want - I only get those from Profile:
# in rails console
>> p = Profile.joins(:user).limit(1)
>> [#<Profile ...>]
>> p.first_name
>> NoMethodError: undefined method `first_name' for #<ActiveRecord::Relation:0x102b521d0> from /Library/Ruby/Gems/1.8/gems/activerecord-3.0.1/lib/active_record/relation.rb:373:in `method_missing' from (irb):8
# I do NOT want to do this (AKA I do NOT want to use "includes")
>> p.user
>> NoMethodError: undefined method `user' for #<ActiveRecord::Relation:0x102b521d0> from /Library/Ruby/Gems/1.8/gems/activerecord-3.0.1/lib/active_record/relation.rb:373:in method_missing' from (irb):9
I want to (efficiently) return an object that has all the properties of Profile and User together. I don't want to :include the user because it doesn't make sense. The user should always be part of the most recent profile as if they were fields within the Profile model. How do I accomplish this?
I think the problem has something to do with the fact that the Profile model doesn't have attributes for User...
Use select() to name the columns you want. At least this works in Rails 3.0.9.
Background: my application has a primary table named :rights. I wanted to be able to ascribe a tag and color to a given :right record so I could easily pick it out of an index listing. This doesn't cleanly fit the Rails picture of associated records; most :rights will never be tagged, and the tags are completely arbitrary (user input via tag/edit).
I could try duplicating the tag data in the :right record, but that violates normal form. Or I could try querying :tags for each :right record, but that is a painfully inefficient approach. I want to be able to join the tables.
MySQL console shows:
mysql> describe rights;
+------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
...
| Tagid | int(11) | YES | | NULL | |
+------------+---------------+------+-----+---------+----------------+
mysql> describe tags;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| TagName | varchar(255) | YES | | NULL | |
| TagColor | varchar(255) | YES | | NULL | |
| created_at | datetime | YES | | NULL | |
| updated_at | datetime | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
I am going to use TagName and TagColor in views/rights/index.html.erb, so I want the rights controller to include those columns in the #rights object it passes to the view. Since not every :right has a :tag, I want to use an outer join:
#rights = Right.joins("LEFT OUTER JOIN tags ON rights.Tagid = tags.id")
But, as everyone has found, this alone doesn't work: a block reference to TagName produces a server error. However, if I add a select at the end, all is well:
#rights = Right.joins("LEFT OUTER JOIN tags ON rights.Tagid = tags.id").select("rights.*,tags.TagName as TagName,tags.TagColor as TagColor")
Note added 6/7/13: the select clause does not require aliases - this works too:
.select("rights.*,tags.TagName,tags.TagColor")
Now I can reference TagName and TagColor in my view:
<% #rights.each do |right| %>
<tr ALIGN=Left <%=
# color background if this is tagged
" BGCOLOR=#{right.TagColor}" if right.TagColor
%> > ...
<% end %>
I don't think that you can load users and profiles with join in Rails. I think that in earlier versions of Rails ( < 2.1) loading of associated models was done with joins, but it was not efficient. Here you have some explanation and links to other materials.
So even if you explicite say that you want to join it, Rails won't map it to associated models. So if you say Profile.whatever_here it will always be mapped to Profile object.
If you still want to do what you said in question, then you can call custom sql query and process results by yourself:
p = ActiveRecord::Base.connection.execute("SELECT * FROM profiles JOIN users ON profiles.user_id = users.id LIMIT 1")
and get results row by row with:
p.fetch_row
It will already be mappet to an array.
Your errors are because you are calling first_name and user method on AciveRecord::Relation object and it stores an array of Profile objects, not a single object. So
p = Profile.joins(:user).limit(1)
p[0].first_name
shoud work.
Better way to fetch only one record is to call:
p = Profile.joins(:user).first
p.first_name
p.user
But when you call p.user it will query database. To avoid it, you can use include, but if you load only one profile object, it is useless. It will make a difference if you load many profiles at a time and want to inlcude users table.
Try using select("*").joins(:table)
In this case, you would type:
User.select("*").joins(:profile)
Hope that works for you.
After reading these tips I got the joins to all be loaded in one query by reading 3 ways to do eager loading (preloading) in Rails 3 & 4.
I'm using Rails 4 and this worked like a charm for me:
refs = Referral.joins(:job)
.joins(:referee)
.joins(:referrer)
.where("jobs.poster_id= ?", user.contact_id)
.order(created_at: :desc)
.eager_load(:job, :referee, :referrer)
Here were my other attempts.
#first attempt
#refs = Referral.joins(:job)
# .where("jobs.poster_id= ?", user.contact_id)
# .select("referrals.*, jobs.*")
# works, but each column needs to be explicitly referenced to be used later.
# also there are conflicts for columns with the same name like id
#second attempt
#refs = ActiveRecord::Base.connection.exec_query("SELECT jobs.id AS job_id, jobs.*, referrals.id as referral_id, referrals.* FROM referrals INNER JOIN jobs ON job_id = referrals.job_id WHERE (jobs.poster_id=#{user.contact_id});")
# this worked OK, but returned back a funky object, plus the column name
# conflict from the previous method remains an issue.
#third attempt using a view + rails_db_views
#refs = JobReferral.where(:poster_id => user.contact_id)
# this worked well. Unfortunately I couldn't use the SQL statement from above
# instead of jobs.* I had to explicitly alias and name each column.
# Additionally it brought back a ton of duplicate data that I was putting
# into an array when really it is nice to work with ActiveRecord objects.
#eager_load
#refs = Referral.joins(:job)
# .where("jobs.poster_id= ?", user.contact_id)
# .eager_load(:job)
# this was my base attempt that worked before I added in two more joins :)
I have got round this problem by creating a VIEW in the database which is the join, and then referencing that as if it were a normal ActiveRecord table in the code. This is fine for getting data out of the database, but if you need to update it, then you'll need to go back to the base classes that represent the 'real' tables. I have found this method to be handy when doing reports that use biggish tables - you can get the data out all in one hit. I am surprised that this doesn't seem to be built into ActiveRecord, seems an obvious thing to me!
So for you:
IN SQL:
CREATE VIEW User_Profiles
AS
SELECT P.*, U.first_name
FROM Users U
inner join Profiles P on U.id=P.user_id
IN RUBY models file:
class UserProfile < ActiveRecord::Base
self.primary_key = :id
#same dependencies as profiles
end
**HINT... I always forget to set the owner of the view (I use postgres), so it blows up straight away with much cursing and self-recrimination.