Noob needs advise on three tables in postgresql DB - sql

I've very minor experience at working with databases( I only know the absolute basics). With that stated I guess my problem is rather easy to solve for more experienced minds.
My question is:
I need a way to be able to search for example "all types of Aluminium and all its sub_materials"?
I can do simple queries like
SELECT *
FROM sub_materials
WHERE category_id = 2;
So what I'm asking for is basically to be able to see the whole branch of Aluminium. I've looked at ltreeand Closure Tables But I'm to much of a Noob to figure it out by my self.
I believe I have to connect the tables somehow as "grandparent, parent, child" or something similar, but I've no idea if there is some other way or?
I'm not even sure if I'm doing this the right way.
Can someone advise me on this?
I've three tables in a database.(below are samples from the tables)
materials_category Holds all the categories for every material in the DB.
materials Holds the Category name and id, material_names and ids and the values for each material.
Some materials has sub_categories. not all but some.
3. sub_materials Holds the category info, material name and id, the sub_material name and id and the sub_material values.
1. `materials_category`
|category_id| category_name|
------------------------------
| 1 | Aggregate |
| 2 | Aluminium |
| 3 | Asphalt |
2. `materials`
|category_id |category_name |material_id |material_name|EE_1|EE_2|EC_1|EC_2
---------------------------------------------------------------------------
| 1 |Aggregate | 1 | General |3 |0 | 52 | 8
| 2 |Aluminium | 2 | General |55 |7 | 9 | 24
| 2 |Aluminium | 3 | Cast Pr |34 |30 | 22 | 28
| 2 |Aluminium | 4 | Extrud. |65 |16 | 8 | 74
| 2 |Aluminium | 5 | Rolled |15 | 0 | 9 | 61
| 3 |Asphalt | 6 |Asphalt, 4% |2 |22 | 6 | 54
| 3 |Asphalt | 7 |Asphalt, 5% |3 |91 | 1 | 4
3. `sub_materials`
|category_id|category_name|material_id|material_name|sub_mar_id|sub_mar_name|EE_1|EE_2|EC_1|EC_2|
|2 |Aluminium |2 |General |1 |Virgin |21 |5 |9 |60 |
|2 |Aluminium |2 |General |2 |Recycled |29 |3 |8 |9 |

Your tables are denormalised. This is usually not a good thing, unless this is a reporting only database.
Anyway, here's a start for what you're after. why don't you run it and give some feedback on whether it's what you want or not.
select *
from materials_category c
left outer join materials m
on c.category_id = m.category_id
left outer join sub_materials s
on s.material_id = m.material_id
where c.category_name = 'Aluminium'
Looking at your data, I'm guessing that 'recycled' could apply to any number of material categories, not just Aluminium? Would you have a whole bunch of 'Recycled' records in your sub_materials to allow for each material_category?

Related

Count string occurrences within a list column SQL/Grafana

I have a table in the following format:
| id | tags |
|----|-------------------------|
|1 |['Car', 'Plane', 'Truck']|
|2 |['Plane', 'Truck'] |
|3 |['Car', 'Plane'] |
|4 |['Plane'] |
|5 |['Boat', 'Truck'] |
How can I create a table that gives me the total number of occurrences of each item in all cells of the "tags" column? Items ideally do not include single quotes, but may if necessary.
The resulting table would look like:
| tag | count |
|-------|-------|
| Car | 2 |
| Plane | 4 |
| Truck | 3 |
| Boat | 1 |
The following does not work because it only counts identical "tags" entries rather than comparing list contents.
SELECT u.id, count(u.tags) as cnt
FROM table u
group by 1
order by cnt desc;
I am aware of this near-identical question, but they are using Snowflake/SQL whereas I am using MySQL/Grafana so the accepted answer uses functions unavailable to me.

How to join two queries with count?

I have 3 tables in database:
Table 1: violation
| violation_id | violation_name |
|:-------------:|:--------------:|
|1 | No Parking |
|2 | Speed Contest |
|3 | No Helmet |
Table 2: violators
| violator_id | violation_id |
|:-------------:|:--------------:|
|1 |1 |
|2 |1 |
|3 |3 |
Table 2: previous_violator
| prev_violator_id| violation_id |
|:---------------:|:--------------:|
|1 |1 |
|2 |2 |
|3 |2 |
This view that I want:
| violation_name | Total |
|:-------------:|:--------------:|
|No Parking | 3 |
|Speed Contest | 2 |
|No Helmet | 1 |
I perform this code that joins the violator table and violation:
SELECT *,count(violators.violation_id) as vid
FROM violators
LEFT JOIN violation ON violation.violation_id = violators.violation_id
LEFT JOIN previous_violator ON previous_violator.violator_id = violators.violator_id
WHERE date_apphrehend BETWEEN '$from' AND '$to'
GROUP BY violators.violation_id
My problem is, I want to join the previous violator table that count to the total based on the violation_name.
You can first union all to get them into a single result and then count(*) it. Finally join with Violation to get names. ie:
select violation_name, count(*) as cnt
from (select violation_id from Violators
union all
select violation_id from previous_Violators) tmp
inner join Violation on tmp.violation_id = Violation.violation_id
group by Violation.violation_id, violation_name;
Sample DBFiddle demo.
PS: Sample is in postgreSQL but it would be the same for most backends. You didn't tag your backend.

How to concatenate multiple result rows of one row into one, based on FK

I am looking for a way to concatenate the result of the table into one row.
I have 4 tables;
Suppliers table
+--+----------------+----------------+
|id|name |hook_name |
+--+----------------+----------------+
|1 |724 |724 |
|2 |Air |air |
|3 |Akustik |akustik |
|4 |Almira |almira |
+--+----------------+----------------+
Supplier Offices;
(label column represents pickup/dropoff string)
+---+-----------+----------+------------+
|id |supplier_id| zip_code | label |
+---+-----------+----------------+------+
|95 |24 |25325 | 344 | <- supplier_id 24 has office location 77,98 (label pickup)
|96 |24 |9535 | 93 | <- same. only label different (label dropoff)
|97 |1 |2858 | 95 |
|98 |1 |50285 | 954 |
|99 |1 |10094 | 24 |
|100|1 |4353 | 59 |
+---+-----------+----------------+------+
OfficeLocations (Pivot table)
+------------------+-----------+
|supplier_office_id|location_id|
+------------------+-----------+
|95 |77 | <- location I want to concatenate `supplier_id = 24` (istanbul)
|96 |98 | <- location I want to concatenate `supplier_id = 24` (london)
|97 |77 |
|98 |77 |
+------------------+-----------+
Locations
+---------------+
|id |name |
+---------------+
|77 |istanbul |
|96 |berlin |
|97 |newyork |
|98 |london |
+---------------+
I want to find the office for the given location information.
I haven't manage to create custom column about label.
If I want to access the office information of locations 1 and 2 I want to get something like this;
+---------------+------------+----------------+
| supplier_id | pickup_label | dropoff_label |
+-------------+--------------+----------------+
| 95 | 344 | 93 |
+-------------+--------------+----------------+
I've been able to get this far right now with my Postgresl SQL.
SELECT supplier_offices.id,
supplier_offices.supplier_id
FROM "supplier_offices"
INNER JOIN suppliers on supplier_offices.supplier_id = suppliers.id
INNER JOIN office_locations on supplier_offices.id = office_locations.supplier_office_id
AND ("office_locations"."location_id" IN (77, 98)
This code works if I understand what you mean. This code works if I understand what you mean. Of course for sql, but with a little change, I think it will work in Postgresl SQL as well
select supplier_offices.id as supplier_id, max(supplier_offices.label) as pickup_label, min(supplier_offices.label) as dropoff_label
from supplier_offices
inner join suppliers on supplier_offices.supplier_id = suppliers.id
inner join office_locations on supplier_offices.id = office_locations.supplier_office_id
where office_locations.location_id in (77,98)
group by supplier_id
I think you want some sort of aggregation. It is entirely unclear hoe pickup locations are identified versus drop off. But, something like this:
SELECT so.supplier_id,
ARRAY_AGG(location_id) FILTER (WHERE so.label in (344)) as pickup,
ARRAY_AGG(location_id) FILTER (WHERE so.label not in (344)) as dropoff
FROM "supplier_offices" so JOIN
office_locations ol
ON so.id = ol.supplier_office_id AND
ol.location_id IN (77, 98)
GROUP BY so.supplier_id;

Querying on EAV SQL Design

I have 3 tables like this.
Entity_Table
|e_id|e_name|e_type |e_tenant|
|1 | Bob | bird | owner_1|
|2 | Joe | cat | owner_1|
|3 | Joe | cat | owner_2|
AttributeValue_Table
|av_id|prop_name |prop_value|
|1 | color | black |
|2 | color | white |
|3 | wing size| 7" |
|4 | whiskers | long |
|5 | whiskers | short |
|6 | random | anything |
Entity_AttrVal
|e_id|av_id|
| 1 | 1 |
| 1 | 3 |
| 2 | 2 |
| 2 | 5 |
| 3 | 1 |
| 3 | 4 |
| 3 | 6 |
What I want to be able to do is something like 'find entity where e_name='Joe' and color=black and whiskers=short.
I can obtain a result set where each row has 1 prop/value, along with the entity information, so querying on one property works. But I need to be able to do arbitrary N properties. How do I do something like this?
Can I build a join table with all properties as columns or something
edit2: Looks like I can do something like this
SELECT et.e_id, et.e_name, et.e_type
FROM Entitiy_table et
LEFT JOIN Entity_AttrVal j ON et.e_id = j.e_id
RIGHT JOIN AttributeValue_Table at ON at.av_id = j.av_id
WHERE (av.prop_name='color' AND av.prop_value='white') OR (av.prop_name='whiskers' AND av.prop_value='long')
GROUP BY et.e_id, et.e_name, et.e_type
HAVING COUNT(*) = 2;
You have to add a predicate for each name/value combination:
SELECT <whatever you need>
FROM Entity_Table et
WHERE et.e_name = 'Joe'
AND EXISTS (SELECT 1
FROM AttributeValue_Table avt
JOIN Entity_AttrVal ea ON ea.e_id = et.e_id
WHERE ea.a_id = avt.av_id
AND avt.prop_name = 'color'
AND avt.prop_value = 'black')
AND EXISTS (SELECT 1
FROM AttributeValue_Table avt
JOIN Entity_AttrVal ea ON ea.e_id = et.e_id
WHERE ea.a_id = avt.av_id
AND avt.prop_name = 'whiskers'
AND avt.prop_value = 'short')
(I apologize if my Sql Server dialect shines through)
To do an arbitrary number of comparisons, you'd have to generate the SQL and execute it.
As said in a comment, this goes to show that EAV is a pain (an anti-pattern, really), but I know by experience that sometimes there's simply no alternative if we're bound to a relational database.

How to Merge 2 Rows into one by comma separate?

I need to merge this individual rows to one column, I now how to merge column by comma separated,
+---------------+-------+-------+
|CID |Flag |Value |
+---------------+-------+-------+
|1 |F |10 |
|1 |N |20 |
|2 |F |12 |
|2 |N |23 |
|2 |F |14 |
|3 |N |21 |
|3 |N |22 |
+---------------+-------+-------+
Desired Result can be anything,
+-----------+----------------------------+ +--------------------------+
|Part Number| Value | | Value |
+-----------+----------------------------+ +--------------------------+
| 1 | 1|F|10 ; 1|N|20 | Or | 1|F|10 ; 1|N|20 |
| 2 | 2|F|12 ; 2|N|23 ; 2|F|14 | | 2|F|12 ; 2|N|23 ; 2|F|14 |
| 3 | 3|N|21 ; 3|N|22 | | 3|N|21 ; 3|N|22 |
+-----------+----------------------------+ +--------------------------+
Note:
Any hint in right direction with small example is more than enough
EDIT :
I have massive data in tables like thousands of records where parent's and child relationship is present. I have to dump this into text files by comma separated values In single line as record. Think as primary record has relationship with so many other table then all this record has to be printed as a big line.
And I am trying to achieve by creating query so load can be distributed on database and only thing i have to worry about in business is just dumping logic into text files or whatever form we need in future.
You can try to use LISTAGG and your query will look like this:
select a.cid, a.cid || listagg(a.flag || '|' || a.value, ',')
from foo.dat a
group by a.cid
You can use different separators and of course play with how the result will be formatted.