Cast and transform generic attribute values to a flat database view - sql

For reasons out of scope of this question I have a relation that persists generic values of an entity and looks something like this:
| group_id::int | id:int |attr_id::text | data_type::text | value::text |
-------------------------------------------------------------------------
| G1 | 1 | A | varchar | lorem |
| G1 | 1 | B | integer | 1001 |
| G2 | 2 | B | integer | 1002 |
data_type is guaranteed to be one of PostgreSQLs supported data types, so the table (or tables that is, example is simplified) somewhat represents a table definition.
I would like to present this transformed in a different view per group, so the values are cast to their actual data types. Is this even possible?
Database View for G1 Database View for G2
| id:int | A::varchar | B::int | | id:int | B::int |
-------------------------------- -------------------
| 1 | lorem | 1001 | | 2 | 1002 |
I was thinking tablefunc.crosstab would be the way to go but I didn't come very far. I'm just out of ideas. Any help or directions are very welcome.

Related

How to design tables to allow for multi-field query on one row

I am very new to database design and am using MS Access to try achieve my task. I am trying to create a database design that will allow for the name and description of two items to be queried
on a single row of information. Here is the problem: certain items are converted to other particular items -
any item can have multiple conversions performed on it, and all conversions will have two (many) items involved.
In this sense, we have a many-to-many relationship which necessitates the use of an intermediate table. My
tables must be structured in a way that allows for me to, in one row, query the Item ID's and names
of which items were involved in conversions.
My current table layout is as follows:
Items
+--------+----------+------------------+--+
| ItemID*| ItemName | ItemDescription | |
+--------+----------+------------------+--+
| 1 | DESK | WOOD, 4 LEG | |
| 2 | SHELF | WOOD, SOLID BASE | |
| 3 | TABLE | WOOD, 4 LEG | |
+--------+----------+------------------+--+
ItemConversions
+------------------+--------------+
| ConversionID(CK) | Item1_ID(CK) |
+------------------+--------------+
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
+------------------+--------------+
Conversions
+---------------+----------+----------+
| ConversionID* | Item1_ID | Item2_ID |
+---------------+----------+----------+
| 1 | 2 | 1 |
| 2 | 2 | 3 |
| 3 | 1 | 3 |
+---------------+----------+----------+
What I want is for it to be possible to achieve the kind of query I described above, though I don't think
my current layout is going to work for this, since the tables are only being joined on Item1_ID. Any advice
would be appreciated, hopefully my tables are not too specific and this is easily understandable.
A sample query output might look like this:
+--------------+----------+----------+----------+----------+
| ConversionID | Item1_ID | ItemName | Item2_ID | ItemName |
+--------------+----------+----------+----------+----------+
| 1 | 2 | SHELF | 1 | DESK |
+--------------+----------+----------+----------+----------+
I got it working how I wanted to with the help of June7's suggestion - I didn't know you could add in tables
multiple times in the query design page (very useful!). As for the tables, I edited the layout so that I have only
Items and Conversions (I deleted ItemConversions). Using the AS sql command I was able to write a query that pulls
the data I want from the tables. The table and query layout can be seen below:
Items
+--------+----------+------------------+--+
| ItemID*| ItemName | ItemDescription | |
+--------+----------+------------------+--+
| 1 | DESK | WOOD, 4 LEG | |
| 2 | SHELF | WOOD, SOLID BASE | |
| 3 | TABLE | WOOD, 4 LEG | |
+--------+----------+------------------+--+
Conversions
+---------------+----------+----------+
| ConversionID* | Item1_ID | Item2_ID |
+---------------+----------+----------+
| 1 | 2 | 1 |
| 2 | 2 | 3 |
| 3 | 3 | 1 |
+---------------+----------+----------+
Query:
SELECT
Conversions.ConversionID,
Conversions.Item1_ID,
Conversions.Item2_ID,
Items.ItemName,
Items_1.ItemName,
FROM
(
Conversions
INNER JOIN
Items
ON Conversions.Item1_ID = Items.ItemID
)
INNER JOIN
Items AS Items_1
ON Conversions.Item2_ID = Items_1.ItemID;

When Querying Many-To-Many Relationship in SQL, Return Multiple Connections As an Array In Single Row?

Basically, I have 3 tables, titles, providers, and provider_titles.
Let's say they look like this:
| title_id | title_name |
|------------|----------------|
| 1 | San Andres |
| 2 |Human Centipede |
| 3 | Zoolander 2 |
| 4 | Hot Pursuit |
| provider_id| provider_name |
|------------|----------------|
| 1 | Hulu |
| 2 | Netflix |
| 3 | Amazon_Prime |
| 4 | HBO_GO |
| provider_id| title_id |
|------------|----------------|
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 3 | 1 |
| 3 | 3 |
| 4 | 4 |
So, clearly there are titles with multiple providers, yeah? Typical many-to-many so far.
So what I'm doing to query it is with a JOIN like the following:
SELECT * FROM provider_title JOIN provider ON provider_title.provider_id = provider.provider_id JOIN title ON title.title_id = provider_title.title_id WHERE provider.name IN ('Netflix', 'HBO_GO', 'Hulu', 'Amazon_Prime')
Ok, now to the actual issue. I don't want repeated title names back, but I do want all of the providers associated with the title. Let me explain with another table. Here is what I am getting back with the current query, as is:
| provider_id| provider_name | title_id | title_name |
|------------|---------------|----------|---------------|
| 1 | Hulu | 1|San Andreas |
| 1 | Hulu | 2|Human Centipede|
| 2 | Netflix | 1|San Andreas |
| 3 | Amazon_Prime | 1|San Andreas |
| 3 | Amazon_prime | 3|Zoolander 2 |
| 4 | HBO_GO | 4|Hot Pursuit |
But what I really want would be something more like
| provider_id| provider_name |title_id| title_name|
|------------|-----------------------------|--------|-----------|
| [1, 2, 3] |[Hulu, Netflix, Amazon_Prime]| 1|San Andreas|
Meaning I only want distinct titles back, but I still want each title's associated providers. Is this only possible to do post-sql query with logic iterating through the returned rows?
Depending on your database engine, there may be an aggregation function to help achieve this.
For example, this SQLfiddle demonstrates the postgres array_agg function:
SELECT t.title_id,
t.title_name,
array_agg( p.provider_id ),
array_agg( p.provider_name )
FROM provider_title as pt
JOIN
provider as p
ON pt.provider_id = p.provider_id
JOIN title as t
ON t.title_id = pt.title_id
GROUP BY t.title_id,
t.title_name
Other database engines have equivalents. For example:
mySQL has group_concat
Oracle has listagg
sqlite has group_concat (as well!)
If your database isn't covered by the above, you can google '[Your database engine] aggregate comma delimited string'

How to add data or change schema to production database

I am new to working with databases and I want to make sure I understand the best way to add or remove data from a database without making a mess of any related data.
Here is a scenario I am working with:
I have a Tags table, with an Identity ID column. The Tags can be selected via the web application to categorize stories that are submitted by a user. When the database was first seeded; like tags were seeded in order together. As you can see all the Campuses (cities) were 1-4, the Colleges (subjects) are 5-7, and Populations are 8-11.
If this database is live in production and the client wants to add a new Campus (City) tag, what is the best way to do this?
All the other city tags are sort of organized at the top, it seems like the only option is to insert any new tags at to bottom of the table, where they will end up taking whatever the next ID available is. I suppose this is fine because the Display category column will allow us to know which categories these new tags actually belong to.
Is this typical? Is there better ways to set up the database or handle this situation such that everything remains more organized?
Thank you
+----+------------------+---------------+-----------------+--------------+--------+----------+
| ID | DisplayName | DisplayDetail | DisplayCategory | DisplayOrder | Active | ParentID |
+----+------------------+---------------+-----------------+--------------+--------+----------+
| 1 | Albany | NULL | 1 | 0 | 1 | NULL |
| 2 | Buffalo | NULL | 1 | 1 | 1 | NULL |
| 3 | New York City | NULL | 1 | 2 | 1 | NULL |
| 4 | Syracuse | NULL | 1 | 3 | 1 | NULL |
| 5 | Business | NULL | 2 | 0 | 1 | NULL |
| 6 | Dentistry | NULL | 2 | 1 | 1 | NULL |
| 7 | Law | NULL | 2 | 2 | 1 | NULL |
| 8 | Student-Athletes | NULL | 3 | 0 | 1 | NULL |
| 9 | Alumni | NULL | 3 | 1 | 1 | NULL |
| 10 | Faculty | NULL | 3 | 2 | 1 | NULL |
| 11 | Staff | NULL | 3 | 3 | 1 | NULL |
+----+------------------+---------------+-----------------+--------------+--------+----------+
The terms "top" and "bottom" which you use aren't really applicable. "Albany" isn't at the "Top" of the table - it's merely at the top of the specific view you see when you query the table without specifying a meaningful sort order. It defaults to a sort order based on the Id or an internal ROWID parameter, which isn't the logical way to show this data.
Data in the table isn't inherently ordered. If you want to view your tags organized by their category, simply order your query by DisplayCategory (and probably by DisplayOrder afterwards), and you'll see your data properly organized. You can even create a persistent View that sorts it that way for your convenience.

SQL query for many-to-many self-join

I have a database table that has a companion many-to-many self-join table alongside it. The primary table is part and the other table is alternate_part (basically, alternate parts are identical to their main part with different #s). Every record in the alternate_part table is also in the part table. To illustrate:
`part`
| part_id | part_number | description |
|---------|-------------|-------------|
| 1 | 00001 | wheel |
| 2 | 00002 | tire |
| 3 | 00003 | window |
| 4 | 00004 | seat |
| 5 | 00005 | wheel |
| 6 | 00006 | tire |
| 7 | 00007 | window |
| 8 | 00008 | seat |
| 9 | 00009 | wheel |
| 10 | 00010 | tire |
| 11 | 00011 | window |
| 12 | 00012 | seat |
`alternate_part`
| main_part_id | alt_part_id |
|--------------|-------------|
| 1 | 5 | // Wheel
| 5 | 1 | // |
| 5 | 9 | // |
| 9 | 5 | // |
| 2 | 6 | // Tire
| 6 | 2 | // |
| ... | ... | // |
I am trying to produce a simple SQL query that will give me a list of all alternates for a main part. The tricky part is: some alternates are only listed as alternates of alternates, it is not guaranteed that every viable alternate for a part is listed as a direct alternate. e.g., if 'Part 3' is an alternate of 'Part 2' which is an alternate of 'Part 1', then Part 3 is an alternate of Part 1 (even if the alternate_part table doesn't list a direct link). The reverse is also true (Part 1 is an alternate of Part 3).
Basically, right now I'm pulling alternates and iterating through them
SELECT p.*, ap.*
FROM part p
INNER JOIN alternate_part ap ON p.part_id = ap.main_part_id
And then going back and doing the same again on those alternates. But, I think there's got to be a better way.
The SQL query I'm looking for will basically give me:
| part_id | alt_part_id |
|---------|-------------|
| 1 | 5 |
| 1 | 9 |
For part_id = 1, even when 1 & 9 are not explicitly linked in the alternates table.
Note: I have no control whatever over the structure of the DB, it is a distributed software solution.
Note 2: It is an Oracle platform, if that affects syntax.
You have to create hierarchical tree , probably you have to use connect by prior , nocycle query
something like this
select distinct p.part_id,p.part_number,p.description,c.main_part_id
from part p
left join (
select main_part_id,connect_by_root(main_part_id) real_part_id
from alternate_part
connect by NOCYCLE prior main_part_id = alternate_part_id
) c
on p.part_id = c.real_part_id and p.part_id != c.main_part_id
order by p.part_id
You can read full documentation about Hierarchical queries at http://docs.oracle.com/cd/B28359_01/server.111/b28286/queries003.htm

SQL multiple properties depending on other attributes

I have the following issue:
I am planning a database with trains. Each train has carriages which divides into compartment and non-compartment. Both of these types has three classes: 1,2,3, and all of them has different amount of places in compartment or in a row.
I could create the following table:
| type | class | seats in a row | rows | seats in a compartment | compartments |
| non-c| 1 | 3 | 18 | NULL | NULL |
| non-c| 2 | 4 | 22 | NULL | NULL |
| non-c| 3 | 5 | 25 | NULL | NULL |
| comp | 1 | NULL | NULL | 6 | 9 |
| comp | 2 | NULL | NULL | 8 | 10 |
| comp | 3 | NULL | NULL | 10 | 11 |
That is, I would set NULL when a property is not connected with a particular type (example number of places in a compartment for a non-compartment car), but in my opinion it is not good looking solution. Do you have any other ideas? Maybe two tables: non-compartment attributes and compartment attributes? However I think that better solution exists.
Like you said, break your design into tables that correspond to logical entities (normalization), that way you will have more scope to accommodate change and less redundant info.
Proposed design
Tables
Tbl_train(Id, other_train_info) -Stores only tran info
Tbl_Carriage(Id, trainid, carriagetypeid, other_carriage_info) - stores carriage info related to a train
Tbl_carriagetype_master(Id, type_desc, class, .. Etc) - stores all the static compartmental info