One to many relationship on the same table - sql

Here is the situation:-
I have a table called Users. This contains user data for students and tutors as most of the data required is the same.
Having completed the system I am now told that the client would like to be able to assign students to tutors.
Is there a legitimate/ clean way I can create a one to many relationship within a single table, perhaps via a link table?
I've tried to think this through but whatever solution I come up with seems messy.
I would be grateful for any input.
Thanks
Phill

Have you tried the following approach?
Make a new table, for example TutorStudent (choose a more appropriate name if needed). It should have two columns:
Tutor_ID
Student_ID
Both columns shall be the (composite) primary key, each column will be a foreign key to your Users table User_ID (I assume this is what you have).
So, if you have a tutor named Newton that has two students, Tesla and Edison, your Users table will have something like this:
User_ID, Name
1, Newton
2, Tesla
3, Edison
and your TutorStudent table will have following values:
Tutor_ID, Student_ID
1, 2
1, 3
Relatively simple and doesn't require any modifications to your existing table.
Do take care when deleting users - use the delete cascade feature of your database system or do some maintenance work afterwards so your TutorStudent table doesn't go stale when updating/removing your users.

My ideal for the same situation
Example: one book have many category:
Basic solution:
book table has recorded book information
category table has recored category information ex: 100 documents
book_category_relation table has single book (book_id) has category(category_id) 1 book may be have 100 category_id
Ideal solution:
First calculate total your category: ex 100 document. Each category equal value 1 bit: max 31 bit so 100 category we have ceil floor(100%31) = 4 groups
category_id = 1 : 1 (1%31) <=> 000000001 group 0 = floor(1/31)
category_id = 2 : 2 (2%31)<=> 000000010 group 0 = floor(2/31)
category_id = 3 : 4 (3%31)<=> 000000100 group 0 = floor(3/31)
category_id = 4 : 8(4%31)<=> 000001000 group 0 = floor(4/31)
...........................
category_id = 31: 2^31(31%31) <=>1000..000 group 0 if moduler 31 equal zero so number group = (31/31 -1)=0;
category_id = 32: 1(32%31) <=> 0000000001 group 1 = floor(32/31)
category_id = 33: 2(33%31) <=> 0000000010 group 1 = floor(33/31)
Ok now we add 4 fields in design book table (group_0,group_1,group_2,group_3) with int(11) unsigned and add index that fields
if book has category id = n so we can the following calculate formula:
bit code = (n%31 ==0)?31: (n%31)
number group field = (n%31==0)?(n/31 -1):floor(n/31)
ex: book in category_id = 100 so:
bit code = (100%31) =7 <=>2^7 = 128,
group = floor(100%31) = 3 <=> in group_3
so if you need query all book in category_id = 100, query string is:
SELECT * FROM book WHERE group_3&128
Note: MySQL not index working if bitwise in where.
But you can check in this link:
Bitwise operations and indexes

Related

Laravel: Adding multiple count results of sub queries

I wanted to add the count result of more than one queries that belong to different tables.
I am using the below problem as a reference to my actual problem because this problem has already a solution (How do I add two count(*) results together on two different tables?) but I am facing problem in implementing the solution in laravel.
I have two tables: Toys and Games.
+--------------------+------------------+
| Field | Type |
+--------------------+------------------+
| toy_id | int(10) unsigned |
| little_kid_id | int(10) unsigned |
+--------------------+------------------+
+--------------------+------------------+
| Field | Type |
+--------------------+------------------+
| game_id | int(10) unsigned |
| little_kid_id | int(10) unsigned |
+--------------------+------------------+
A little kid can have multiple toys. A little kid can be participating in multiple games at once.
I want a query that will give me the total number of toys + games that a little_kid is involved with.
Basically, I want the sum of these two queries:
SELECT COUNT(*) FROM Toys WHERE little_kid_id = 900;
SELECT COUNT(*) from Games WHERE little_kid_id = 900
The above problem has the following accepted answer
SELECT
(SELECT COUNT(*) FROM Toys WHERE little_kid_id = 900)+
(SELECT COUNT(*) from Games WHERE little_kid_id = 900)
AS SumCount
I wanted to implement the above solution in Laravel.
I have tried the following method but to no avail. It gives syntax error.
$sub=DB::tabel('toys')->select(DB::raw('count(*) as total'))
->where('little_kid_id',$id);
$sub1=DB::tabel('games')->select(DB::raw('count(*) as total'))
->where('little_kid_id',$id);
$result = DB::table( DB::raw("( ({$sub->toSql()})+({$sub1->toSql()}) ) as
total_count") )
->mergeBindings($sub)
->mergeBindings($sub1)
->select('total_count.total')
->get();
I have also tried this method. It works but gives collection but I need an integer value of total count
$result=$sub->unionAll($sub1);
dd($result->get());
In short I wanted to perform the accepted solution of that problem (How do I add two count(*) results together on two different tables?) in laravel.
You can use those codes :
$q = DB::tabel('toys')->select('toy_id','little_kid_id')->where('little_kid_id',900);
$q1 = DB::tabel('games')->select('game_id','little_kid_id')->where('little_kid_id',900)
$data = $q->union($q1)->count();
Don't forget Union require the tables must have the same columns so that I select the columns if your columns will not match each other then don't touch the select statement otherwise feel free to remove the codes
The second way is :
DB::table(DB::raw('(SELECT COUNT(*) as c FROM Toys WHERE little_kid_id = 900) t,(SELECT COUNT(*) as c1 from Games WHERE
little_kid_id = 900) t2'))->selectRaw('t.c+t2.c1 as
SumCount')->toSql(); //change toSql() to get() if you want to get
datas instead of sql code
You can try this. for more information look at https://laravel.com/docs/5.8/queries#raw-expressions. if you want to get request parameters by any user then be confirmed to prevent SQL Injection

Multiple occurances of the same Column variable in a query

I have two tables
TicketsForSale
ticket_id (PK)
type
category
Transactions
transaction_id (PK)
ticket_id (FK)
I want to get the transactions per type of tickets. This is what I've tried:
SELECT ticketsforsale.type
, COUNT(transactions.ticket_id)
FROM ticketsforsale
INNER JOIN transactions ON ticketsforsale.ticket_id = transactions.ticket_id
GROUP BY ticketsforsale.type
What I hope for as a result is something like this
{
Sports 5
Theater 7
Cruise 8
Cinema 10
}
But instead I get the following :
{ Theater 2
Cruise 1
Sports 1
Sports 2
Cruise 3
Cinema 5
}
The numbers aren't accurate, just used for demonstration.
(The category column is listing the specific show you attend by "purchasing" the ticket. E.G If the type is "Sports", the category could be Basketball or Football or Volleyball etc. etc. ) I just thought that this column could somehow be the issue here, but maybe I'm wrong.
Try this:
select distinct type
, encode(type::bytea,'hex') hex_type
from TicketsForSale order by 1;
You'll probably find that you have multiple type values that appear identical but have different hexadecimal representations. Fix those discrepancies, and the you should be good to go.

PostgreSQL: Self-referencing, flattening join to table which contains tree of objects

I have a relatively large (as in >10^6 entries) table called "things" which represent locateable objects, e.g. countries, areas, cities, streets, etc. They are used as a tree of objects with a fixed depth, so the table structure looks like this:
id
name
type
continent_id
country_id
city_id
area_id
street_id
etc.
The association inside "things" is 1:n, i.e. a street or area always belongs to a defined city and country (not two or none); the column city_id for example contains the id of the "city" thing for all the objects which are inside that city. The "type" column contains the type of thing (Street, City, etc) as a string.
This table is referenced in another table "actions" as "thing_id". I am trying to generate a table of action location statistics showing the number of active and inactive actions a given location has. A simple JOIN like
SELECT count(nullif(actions.active, 1)) AS icount,
count(nullif(actions.active, 0)) AS acount,
things.name AS name, things.id AS thing_id, things.city_id AS city_id
FROM "actions"
LEFT JOIN things ON actions.thing_id = things.id
WHERE UPPER(substring(things.name, 1, 1)) = UPPER('A')
AND actions.datetime_at BETWEEN '2012-09-26 19:52:14' AND '2012-10-26 22:00:00'
GROUP BY things.name, things.id ORDER BY things.name
will give me a list of "things" (starting with 'A') which have actions associated with them and their active and inactive count like this:
icount | acount | name | thing_id | city_id
------------------------------------------------------------------
0 5 Brooklyn, New York City | 25 | 23
1 0 Manhattan, New York City | 24 | 23
3 2 New York City | 23 | 23
Now I would like to
only consider "city" things (that's easy: filter by type in "things"), and
in the active/inactive counts, use the sum of all actions happening in this city - regardless of whether the action is associated with the city itself or something inside the city (= having the same city_id). With the same dataset as above, the new query should result in
icount | acount | name | thing_id | city_id
------------------------------------------------------------------
4 7 New York City | 23 | 23
I do not need the thing_id in this table (since it would not be unique anyway), but since I do need the city's name (for display), it is probably just as easy to also output the ID, then I don't have to change as much in my code.
How would I have to modify the above query to achieve this? I'd like to avoid additional trips to the database, and advanced SQL features such as procedures, triggers, views and temporary tables, if possible.
I'm using Postgres 8.3 with Ruby 1.9.3 on Rails 3.0.14 (on Mac OS X 10.7.4).
Thank you! :)
You need to count actions for all things in the city in an independent subquery and then join to a limited set of things:
SELECT c.icount
,c.acount
,t.name
,t.id AS thing_id
,t.city_id
FROM (
SELECT t.city_id
,count(nullif(a.active, 1)) AS icount
,sum(a.active) AS acount
FROM things t
LEFT JOIN actions a ON a.thing_id = t.id
WHERE t.city_id = 23 -- to restrict results to one city
GROUP BY t.city_id
) c -- counts per city
JOIN things t USING (city_id)
WHERE t.name ILIKE 'A%'
AND t.datetime_at BETWEEN '2012-09-26 19:52:14'
AND '2012-10-26 22:00:00'
ORDER BY t.name, t.id;
I also simplified a number of other things in your query and used table aliases to make it easier to read.

Postgres update with an inner join across 3 tables?

This morning I asked this very similar question, and it was answered beautifully.
However, after reviewing the play, I see that my actual problem is slightly more complicated than what I described in that question. Basically, I have 3 Postgres tables:
[myschema].[animals]
--------------------
animal_id
animal_attrib_type_id (foreign key to [myschema].[animal_attrib_types])
animal_attrib_value_id (foreign key to [myschema].[animal_attrib_values])
[myschema].[animal_attrib_types]
--------------------------------
animal_attrib_type_id
animal_attrib_type_name
[myschema].[animal_attrib_values]
--------------------------------
animal_attrib_value_id
animal_attrib_value_name
So, I might have an animal record like so:
[myschema].[animals]
--------------------
animal_id = 458
animal_attrib_type_id = 38
animal_attrib_value_id = 23
And the corresponding animal_attrib_type (with id = 38) has the following values:
[myschema].[animal_attrib_types]
--------------------------------
animal_attrib_type_id = 38
animal_attrib_type_name = 'animals.should-make-noise'
And the corresponding animal_attrib_value (with id = 23) has the following values:
[myschema].[animal_attrib_values]
--------------------------------
animal_attrib_type_id = 23
animal_attrib_type_name = 'true'
So, the same animal record can have multiple type/value pairs. In this case the animal had an animal_attrib_type_name of "animals.should-make-noise" corresponding to an animal_attrib_value_name of "true".
At runtime, I will only have the animal_id (i.e, 458) and animal_attrib_type_id (i.e, 38). I need to be able to look up the appropriate animal_attrib_value_name corresponding to that given animal_id and animal_attrib_type_id only, and then update its value to some static text ('true' or 'false'); all from within the same UPDATE statement.
The answer in the above-referenced question was correct for the problem I stated, but since the same animal has 0+ type/value combos I actually need a slightly different SQL statement. Thanks in advance!
Make use of the FROM clause in the PostgreSQL UPDATE command. This is usually cleaner and faster.
UPDATE animal_attrib_values av
SET animal_attrib_value_name = 'true'
FROM animals a
WHERE a.animal_id = 458
AND a.animal_attrib_type_id = 38
AND a.animal_attrib_value_id = av.animal_attrib_value_id;
Since we already know the animal_attrib_type_id we don't have to include the third table animal_attrib_types at all. We could join to it additionally if needed ...
Also, do not table-qualify SET items in an UPDATE. That's a syntax error. I quote the manual on said page:
Do not include the table's name in the specification of a target
column — for example, UPDATE tab SET tab.col = 1 is invalid.
Bold emphasis mine.
The below SQL should do what you are asking:
UPDATE animal_attrib_values aav
SET animal_attrib_value_name= 'true'
WHERE aav.animal_attrib_value_id = (
SELECT a.animal_attrib_value_id
FROM animals a
WHERE a.animal_id = 458
AND a.animal_attrib_type_id = 38
)
;

Microsoft Access 2010 - Updating Multiple Rows with Different values in ONE query

I have a question about updating multiple rows with different values in MS Access 2010.
Table 1: Food
ID | Favourite Food
1 | Apple
2 | Orange
3 | Pear
Table 2: New
ID | Favourite Food
1 | Watermelon
3 | Cherries
Right now, it looks deceptively simple to execute them separately (because this is just an example). But how would I execute a whole lot of them at the same time if I had, say, 500 rows to update out of 1000 records.
So what I want to do is to update the "Food" table based on the new values from the "New" table.
Would appreciate if anyone could give me some direction / syntax so that I can test it out on MS Access 2010. If this requires VBA, do provide some samples of how I should carry this out programmatically, not manually statement-by-statement.
Thank you!
ADDENDUM (REAL DATA)
Table: Competitors
Columns: CompetitorNo (PK), FirstName, LastName, Score, Ranking
query: FinalScore
Columns: CompetitorNo, Score, Ranking
Note - this query is a query of another query, which in turn, is a query of another query (could there be a potential problem here? There are at least 4 queries before this FinalScore query is derived. Should I post them?)
In the competitors table, all the columns except "Score" and "Ranking" are filled. We would need to take the values from the FinalScore query and insert them into the relevant competitor columns.
Addendum (Brief Explanation of Query)
Table: Competitors
Columns: CompetitorNo (PK), FirstName, LastName, Score, Ranking
Sample Data: AX1234, Simpson, Danny, <blank initially>, <blank initially>
Table: CompetitionRecord
Columns: EventNo (PK composite), CompetitorNo (PK composite), Timing, Bonus
Sample Data1: E01, AX1234, 14.4, 1
Sample Data2: E01, AB1938, 12.5, 0
Sample Data3: E01, BB1919, 13.0, 2
Event No specifies unique event ID
Timing measures the time taken to run 200 metres. The lesser, the better.
Bonus can be given in 3 values (0 - Disqualified, 1 - Normal, 2 - Exceptional). Competitors with Exceptional are given bonus points (5% off their timing).
Query: FinalScore
Columns: CompetitorNo (PK), Score, Ranking
Score is calculated by wins. For example, in the above event (E01), there are three competitors. The winner of the event is BB1919. Winners get 1 point. Losers don't get any points. Those that are disqualified do not receive any points as well.
This query lists the competitors and their cumulative scores (from a list of many events - E01, E02, E03 etc.) and calculates their ranking in the ranking column everytime the query is executed. (For example, a person who wins the most 200m events would be at the top of this list).
Now, I am required to update the Competitors table with this information. The query is rather complex - with all the grouping, summations, rankings and whatnots. Thus, I had to create multiple queries to achieve the end result.
How about:
UPDATE Food
INNER JOIN [New]
ON Food.ID=New.ID
SET Food.[Favourite Food] = New.[Favourite Food]