I have an activity model
id | activity_model | activity_type | user_id | activity_id | created_at | updated_at|
1 | Article | Created | 1 | 25 | a time | a time |
2 | Headline Story | Created | 1 | 26 | a time | a time |
**activity_model is a string
Now lets say my controller has (actually cleaner as the second portion is in the model):
#user_activity = Activity.where(user_id: 1).all
I would like my view to be something like this:
- #user_activity.each do |activity|
- if activity_model == "Article"
= link_to Article.find(activity.activity_id).name, activity
elsif activity_model == "Headline Story"
= link_to HeadlineStory.find(activity.activity_id).name, activity
= activity.activity_type
= "#{time_ago_in_words(activity.created_at)} ago"
I am focusing on the if statement portion of the block above.
The question I have is. As I am iterating through these different model types how do I get my link_to's to be clean? I'm using find in the view, that can't be right..
If you want to iterate through Activity objects and find Article objects at the same time you could do this in a couple of ways:
Populate a hash table in the controller and use it in the view.
Create a View model which you populate within the controller and use it to render your view.
Create a helper function which you can use within the view.
The first and last options are the fastest to implement.
Related
In short; we are trying to return certain results from one table based on second level criteria of another table.
I have a number of source data tables,
So:
Table DataA:
data_id | columns | stuff....
-----------------------------
1 | here | etc.
2 | here | poop
3 | here | etc.
Table DataB:
data_id | columnz | various....
-----------------------------
1 | there | you
2 | there | get
3 | there | the
4 | there | idea.
Table DataC:
data_id | column_s | others....
-----------------------------
1 | where | you
2 | where | get
3 | where | the
4 | where | idea.
Table DataD: etc. There are more and more will be added ongoing
And a relational table of visits, where there are "visits" to some of these other data rows in these other tables above.
Each of the above tables holds very different sets of data.
The way this is currently structured is like this:
Visits Table:
visit_id | reference | ref_id | visit_data | columns | notes
-------------------------------------------------------------
1 | DataC | 2 | some data | etc. | so this is a reference
| | | | | to a visit to row id
| | | | | 2 on table DataC
2 | DataC | 3 | some data | etc. | ...
3 | DataB | 4 | more data | etc. | so this is a reference
| | | | | to a visit to row id
| | | | | 4 on table DataB
4 | DataA | 1 | more data | etc. | etc. etc.
5 | DataA | 2 | more data | etc. | you get the idea
Now we currently list the visits by various user given criteria, such as visit date.
however the user can also choose which tables (ie data types) they want to view, so a user has to tick a box to show they want data from DataA table, and DataC table but not DataB, for example.
The SQL we currently have works like this; the column list in the IN conditional is dynamically generated from user choices:
SELECT visit_id,columns, visit_data, notes
FROM visits
WHERE visit_date < :maxDate AND visits.reference IN ('DataA','DataC')
The Issue:
Now, we need to go a step beyond this and list the visits by a sub-criteria of one of the "Data" tables,
So for example, DataA table has a reference to something else, so now the client wants to list all visits to numerous reference types, and IF the type is DataA then to only count the visits if the data in that table fits a value.
For example:
List all visits to DataB and all visits to DataA where DataA.stuff = poop
The way we currently work this is a secondary SQL on the results of the first visit listing, exampled above. This works but is always returning the full table of DataA when we only want to return a subset of DataA but we can't be exclusive about it outside of DataA.
We can't use LEFT JOIN because that doesn't trim the results as needed, we can't use exclusionary joins (RIGHT / INNER) because that then removes anything from DataC or any other table,
We can't find a way to add queries to the WHERE because again, that would loose any data from any other table that is not DataA.
What we kind of need is a JOIN within an IF/CASE clause.
Pseudo SQL:
SELECT visit_id,columns, visit_data, notes
FROM visits
IF(visits.reference = 'DataA')
INNER JOIN DataA ON visits.ref_id = DataA.id AND DataA.stuff = 'poop'
ENDIF
WHERE visit_date < 2020-12-06 AND visits.reference IN ('DataA','DataC')
All criteria in the WHERE clause are set by the user, none are static (This includes the DataA.stuff criteria too).
So with the above example the output would be:
visit_id | reference | ref_id | visit_data | columns | notes
-------------------------------------------------------------
1 | DataC | 2 | some data | etc. |
2 | DataC | 3 | some data | etc. |
5 | DataA | 1 | more data | etc. |
We can't use Union because the different Data tables contain lots of different details.
Questions:
There may be a very straightforward answer to this but I can't see it,
How can we approach trying to achieve this sort of partial exclusivity?
I suspect that our overarching architecture structure here could be improved (the system complexity has grown organically over a number of years). If so, what could be a better way of building this?
What we kind of need is a JOIN within an IF/CASE clause.
Well, you should know that's not possible in SQL.
Think of this analogy to function calls in a conventional programming language. You're essentially asking for something like:
What we need is a function call that calls a different function depending on the value you pass as a parameter.
As if you could do this:
call $somefunction(argument);
And which $somefunction you call would be determined by the function called, depending on the value of argument. This doesn't make any sense in any programming language.
It is similar in SQL — the tables and columns are fixed at the time the query is parsed. Rows of data are not read until the query is executed. Therefore one can't change the tables depending on the rows executed.
The simplest answer would be that you must run more than one query:
SELECT visit_id,columns, visit_data, notes
FROM visits
INNER JOIN DataA ON visits.ref_id = DataA.id AND DataA.stuff = 'poop'
WHERE visit_date < 2020-12-06 AND visits.reference = 'DataA';
SELECT visit_id,columns, visit_data, notes
FROM visits
WHERE visit_date < 2020-12-06 AND visits.reference = 'DataC';
Not every task must be done in one SQL query. If it's too complex or difficult to combine two tasks into one query, then leave them separate and write code in the client application to combine the results.
I'm very new to Access and my teacher is... hard to follow. So I feel like there's something pretty basic I'm probably missing here. I think the biggest problem I'm having with this question is that I'm struggling to find the words to communicate what I actually need to do, which is really putting a damper on my google-fu.
In terms of what I think I want to do, I want to make a record reference another table in its entirety.
Main
+----+-------+--------+-------+----------------------------+
| PK | Name | Phone# | [...] | Cards |
+----+-------+--------+-------+----------------------------+
| 1 | Bob | [...] | [...] | < Reference to 2nd table > |
| 2 | Harry | [...] | [...] | [...] |
| 3 | Ted | [...] | [...] | [...] |
+----+-------+--------+-------+----------------------------+
Bob's Cards
+----+-------------+-----------+-------+-------+-------+
| PK | Card Name | Condition | Year | Price | [...] |
+----+-------------+-----------+-------+-------+-------+
| 1 | Big Slugger | Mint | 1987 | .20 | [...] |
| 2 | Quick Pete | [...] | [...] | [...] | [...] |
| 3 | Mac Donald | [...] | [...] | [...] | [...] |
+----+-------------+-----------+-------+-------+-------+
This would necessitate an entire new table for each record in the main table though, if it's even possible.
But the only alternative solution I can think of is to add 'Card1, Condition1, [...], Card2, Condition2, [...], Card3, [...]' fields to the main table and having to add another set of fields any time someone increases the maximum number of cards stored.
So I'm sort of left believing there is some other approach I should be taking that our teacher has failed to properly explain. We haven't even touched on forms and reports yet so I don't need to worry about working them in.
Any pointers?
(Also, the entirety of this data and structure is only a rough facsimile of my own, as I'd rather learn how to do it and apply it myself than be like 'here's my data, pls fix.')
Third option successfully found in comments by the helpful Minty.
This depends on a number of things, however to keep it simple you
would normally add one field to the cards table, with an number data
type called CardOwnerID. In your example it would be 1 indicating Bob.
This is known as a foreign key. (FK) - However if you have a table of
cards and multiple possible owners then you need a third table - a
Junction table. This would consist of the Main Person ID and the Card
ID. – Minty
I have a database which contains a lot of data and although I was not involved in setting it up it is what I have to work with.
Within this database is somewhat of a lookup table. However, this table has no link to any other tables. It essentially takes the following form
ID | input | table_name |
-------------------------------------
1 | Movie | movie_tbl |
2 | Cartoon | cartoon_tbl |
3 | Animation | cartoon_tbl |
4 | Audio | audio_tbl |
5 | Picture | picture_tbl |
The table is a lot larger than the above, but the structure is as above. So what happens is someone visits my site. Here, they have an input field. Say they enter Movie then the above table is called to find the input with Movie. It then gets what table it needs to look in. I would imagine that the query would be something like
SELECT table_name FROM lookup_table WHERE input LIKE Movie;
Now that should return movie_tbl. I now know that I need to search for Movie within movie_tbl and return all the data for its row. So movie_tbl might be like this (data would be some type of data and the column names different)
ID | input | col_1 | col_2 | col_3 |
----------------------------------------------------
1 | Movie | data | data | data |
2 | Cartoon | data | data | data |
3 | Animation | data | data | data |
4 | Audio | data | data | data |
5 | Picture | data | data | data |
So now my query will be something like this
SELECT * FROM movie_tbl WHERE input LIKE Movie;
Now the tables have tens of thousands of lines of data. My real question is whether the above will be effecient or not? With the database I was given however, I do not see any other way I could do this (I cant touch the database). Is there anything I can do to make this more effecient?
Any advice appreciated
Thanks
Why are you checking for input in the 2nd table? You have already filtered the input from the first table:
SELECT table_name FROM lookup_table WHERE input LIKE Movie;
In this case you dont have to make 2 queries. Just the 2nd one should suffice. Or just having Movie data in the 2nd table and separate tables for Cartoon, Animation etc. Because then you wont be accessing the 'WHERE' clause, just:
SELECT * FROM movie_tbl;
2nd Suggestion: Use = instead of LIKE. No need for pattern matching if you know the exact input string.
I wanted to ask you which could be the best approach creating my MySQL database structure having the following case.
I've got a table with items, which is not needed to describe as the only important field here is the ID.
Now, I'd like to be able to assign some attributes to each item - by its ID, of course. But I don't know exactly how to do it, as I'd like to keep it dynamic (so, I do not have to modify the table structure if I want to add a new attribute type).
What I think
I think - and, in fact, is the structure that I have right now - that I can make a table items_attributes with the following structure:
+----+---------+----------------+-----------------+
| id | item_id | attribute_name | attribute_value |
+----+---------+----------------+-----------------+
| 1 | 1 | place | Barcelona |
| 2 | 2 | author_name | Matt |
| 3 | 1 | author_name | Kate |
| 4 | 1 | pages | 200 |
| 5 | 1 | author_name | John |
+----+---------+----------------+-----------------+
I put data as an example for you to see that those attributes can be repeated (it's not a relation 1 to 1).
The problem with this approach
I have the need to make some querys, some of them for statistic purpouses, and if I have a lot of attributes for a lot of items, this can be a bit slow.
Furthermore - maybe because I'm not an expert on MySQL - everytime I want to make a search and find "those items that have 'place' = 'Barcelona' AND 'author_name' = 'John'", I end up having to make multiple JOINs for every condition.
Repeating the example before, my query would end up like:
SELECT *
FROM items its
JOIN items_attributes attr
ON its.id = attr.item_id
AND attr.attribute_name = 'place'
AND attr.attribute_value = 'Barcelona'
AND attr.attribute_name = 'author_name'
AND attr.attribute_value = 'John';
As you can see, this will return nothing, as an attribute_name cannot have two values at once in the same row, and an OR condition would not be what I'm searching for as the items MUST have both attributes values as stated.
So the only possibility is to make a JOIN on the same repeated table for every condition to search, which I think it's very slow to perform when there are a lot of terms to search for.
What I'd like
As I said, I'd like to be able to keep the attributes types dynamical, so by adding a new input on 'attribute_name' would be enough, without having to add a new column to a table. Also, as they are 1-N relationship, they cannot be put in the 'items' table as new columns.
If the structure, in your opinion, is the only one that can acheive my interests, if you could light up some ideas so the search queries are not a ton of JOINs it would be great, too.
I don't know if it's quite hard to get it as I've been struggling my head until now and I haven't come up with a solution. Hope you guys can help me with that!
In any case, thank you for your time and attention!
Kind regards.
You're thinking in the right direction, the direction of normalization. The normal for you would like to have in your database is the fifth normal form (or sixth, even). Stackoverflow on this matter.
Table Attribute:
+----+----------------+
| id | attribute_name |
+----+----------------+
| 1 | place |
| 2 | author name |
| 3 | pages |
+----+----------------+
Table ItemAttribute
+--------+----------------+
| item_id| attribute_id |
+--------+----------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
+--------+----------------+
So for each property of an object (item in this case) you create a new table and name it accordingly. It requires lots of joins, but your database will be highly flexible and organized. Good luck!
In my Opinion it should be something like this, i know there are a lot of table, but actually it normilizes your DB
Maybe that is why because i cant understant where you get your att_value column, and what should contains this columns
I'd like to know how can I make a unique query to find which values exist and which values do not. I explain.
What I have
I've got a database table with a structure as follows:
+----+--------+-----------+-----------+
| id | action | button_id | type |
+----+--------+-----------+-----------+
| 1 | 1 | 1 | button |
| 2 | 2 | 4 | button |
| 3 | 1 | 2 | attribute |
+----+--------+-----------+-----------+
As you can see, an action can have multiple button_id values. For your knowledge, a button_id can be assigned to multiple action, too, but a button_id can only have a type for an action.
So, button_id 1 can be also present in action 4 with the type "attribute" set to it, but it cannot be duplicated to the same action with another type.
The problem
The problem comes when I want to update the buttons in an action. I receive an action object with an array of the buttons it have (in PHP) with the structure below (I write it in JSON structure):
"buttons":
[
{
"id":"1",
"type":"button"
},
{
"id":"3",
"type":"attribute"
}
]
As you can see, the button with ID 1 remains the same, but I've got a new button to deal with (the button with ID 3) and the button with ID 2 is not present anymore.
What I'd want
I'd want to be able to make a unique MySQL query that returns me which values from those I receive exists and which do not, and which may be present in the database but not in that array.
To sum up: I want to know the differences between the buttons in the array received and those present in the database.
So, as an example with the received data described before and the database as we have it right now, I expect to receive something like this:
+--------+-----------+--------+------------+
| action | button_id | exists | is_present |
+--------+-----------+--------+------------+
| 1 | 1 | 1 | 1 |
| 1 | 2 | 1 | 0 |
| 1 | 3 | 0 | 1 |
+--------+-----------+--------+------------+
With this information, I'd be able to know that button with ID 2 does not exist anymore (because it's not present in the new array) and button with ID 3 is a new button because it does not exists previously but it's present in the new array.
What I've tried
There are some tests I've tried, but none of them gives me what I need, and not only tested with MySQL pure queries.
For example, I've tried to check the existence for each button I receive but that would leave me without being able to find if a button is deleted (so it's not present in the received array).
Checking that but taking as reference the buttons in the database has the same effect, as I will be able to check which have been updated or deleted, but it would skip those that are new and not present in the database.
I've tried to write some queries making COUNT queries and GROUP BY button_id, and so, but no luck neither.
(I won't write the queries because none of them have given me the expected results, so they won't be of any help for you).
Any combination of those explained before I think will be much slower than doing it purely by database queries, and that's why I'm asking for it.
The question
Is there a query that would return to me something like the response explained before in "What I'd want" section, so it would make only a call to the MySQL server?
Thank you all for your time, your responses and your patience for any lack of information you may find by my part.
Of course, any doubts, questions you have or information you may need, comment it and I'll try to explain it better or to add it.
Kind regards.
To do that in a single query would be very cubersome. Here is a solution that is not exactly what you are looking for but should do the job.
Let's say your table looks like this :
CREATE TABLE htmlComponent
(
id int auto_increment primary key,
action int,
button_id int not null,
type varchar(20),
dtInserted datetime,
dtUpdated datetime
);
CREATE UNIQUE INDEX buttonType ON htmlComponent(button_id, type);
Now we need to update the table according to the buttons / atributes you have for a specific action.
-- Reset dtInserted and dtUpdated for action 1
UPDATE htmlComponent SET dtInserted = null, dtUpdated = null WHERE action=1;
-- INSERT or UPDATE according to the data inside the json structure
INSERT INTO htmlComponent (action, button_id, type, dtInserted)
VALUES
(1, 1, 'button', NOW()),
(1, 3, 'attribute', NOW())
ON DUPLICATE KEY UPDATE
button_id = VALUES(button_id),
type = VALUES(type),
dtInserted = null,
dtUpdated = NOW();
-- Getting the result
SELECT * FROM htmlComponent where action=1;
Your should end up with this result which will make it easy to figure out what doesn't exists anymore, what is new and what was updated.
+----+--------+-----------+-----------+----------------------------+----------------------------+
| ID | ACTION | BUTTON_ID | TYPE | DTINSERTED | DTUPDATED |
+----+--------+-----------+-----------+----------------------------+----------------------------+
| 1 | 1 | 1 | button | (null) | February, 09 2015 16:21:49 |
| 3 | 1 | 2 | attribute | (null) | (null) |
| 4 | 1 | 3 | attribute | February, 09 2015 16:21:49 | (null) |
+----+--------+-----------+-----------+----------------------------+----------------------------+
Here is a fiddle. Please note I had to put the UPDATE and the INSERT in the left panel because DML are not allowed in the query panel.