SQL join multiple columns into different rows - sql

i have two tables name Combine and Product , combine has multiple products inside and combine has strictly 3 products so that i have two tables strutred as below:
Combine | id | name | image | item1 | item2 | item3
1 | exmpl | www.exmpl.com/exmp.jpg | 3 | 2 | 16
Product | id | name | image | stock
2 | productexmpl | www.product.com/product2.jpg | 3
3 | productexmpl2 | www.product.com/product3.jpg | 7
16 | productexmpl3 | www.product.com/product16.jpg | 3
What i want to get is search by combine id like SELECT * FROM Combine Where id = '' and get combine products in different rows , what i've tried is join tables with SELECT * FROM Combine as c JOIN Product as p on c.item1 = p.id AND c.item2 = p.id AND c.item3 = p.id but it joins information horizontally what i want is to get information vertically which means in different rows as below
id | name | image | stock
2 | productexmpl | www.product.com/product2.jpg | 3
3 | productexmpl2 | www.product.com/product3.jpg | 7
16 | productexmpl3 | www.product.com/product16.jpg | 3
i dont know if the structre is wrong by design but any help is appreciated
Thank you

you can try this query:
SELECT p.id, p.name, p.image, p.stock
FROM Combine as c
inner join
Product as p
on (c.item1=p.id or c.item2=p.id or c.item3=p.id)
Where id = ###

Related

SQL generate Data based of the ids of three tables

I have three tables store, gender, age_group each of these tables have ids. I need to generate table data for each one all possible combinations of the three.
ex. store_id = (1,2,3) gender_id = (1,2,3) age_group_id = (1,2,3)
so that i have a table that looks like this:
|store_id|gender_id|age_group_id|
|:------:|:-------:|:----------:|
| 1 | 1 | 1 |
| 1 | 2 | 1 |
| 1 | 3 | 1 |
| 2 | 1 | 3 |
| 2 | 2 | 3 |
| 3 | 1 | 3 |
| 3 | 2 | 3 |
etc. continuing on until each combination is populated, any suggestions on best approach to do this in SQL
Cross join the three tables:
select
s.Id as store_id,
g.Id as gender_id,
a.Id as age_group_id
from store s
cross join gender g
cross join age_group a

Using COUNT in INNER JOIN in SQLite (to count absent records)

In SQLite, I have a table named Items:
| id | name |
|----|-------|
| 1 | .. |
| 2 | .. |
| 3 | .. |
| 4 | .. |
| 5 | .. |
| 6 | .. |
Each item might have tags associated with it. Tags are stored in another table, and have their own IDs. The association of items and tags is stored in a third table called Associations:
| itemID | tagID |
|--------|--------|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 3 | 4 |
| 3 | 5 |
| 4 | 5 |
As you can see, item 1 has 3 tags, items 2 and 4 have 1 tag each, and item 3 has 2 tags. Items 5 and 6 have no tags, so their IDs do not appear in the Associations table.
Now, I want to retrieve IDs of items having less than, say, 2 tags, including zero tags. If it were only about the Associations table, the task is easy:
SELECT itemID FROM Associations GROUP BY itemID HAVING COUNT(tagID) < 3;
But this will not return IDs of items which do not have tags, as they are absent in the Associations table. So I guess I have to use JOIN somehow. I tried
SELECT id FROM Items INNER JOIN Associations ON Associations.itemID=Items.id GROUP BY itemID HAVING COUNT(Associations.tagID) < 3;
But this seems to do the same as the previous query.
Help is appreciated :)
EDIT: So, the answer is just to replace INNER JOIN with LEFT JOIN and GROUP BY itemID with GROUP BY Items.id.
QUESTION EXTENDED: Now, to tell the secret, the tags (apart from having IDs) fall into different categories, say, red, green and blue (so, e.g., red tag with id 5 and green tag with id 5 are in fact different tags).
| itemID | tagID |tagType|
|--------|--------|-------|
| 1 | 1 | red |
| 1 | 2 | red |
| 1 | 3 | blue |
| 2 | 1 | green |
| 3 | 4 | blue |
| 3 | 5 | red |
| 4 | 5 | blue |
So I need to retrieve IDs of items which have less than N tags of specific type. I thought I could easily solve this by using the Yogesh's answer by adding the WHERE clause like this:
SELECT i.id AS itemID
FROM Items i LEFT JOIN
Associations a
ON a.itemID = i.id
WHERE a.tagType='red'
GROUP BY i.id
HAVING COUNT(a.tagID) < 3;
But this again fails to return items which do not have tags at all, because they are now filtered out by the WHERE condition. I think I need something along these lines:
SELECT i.id AS itemID
FROM Items i LEFT JOIN
Associations a
ON a.itemID = i.id
GROUP BY i.id
HAVING COUNT(a.tagID WHERE a.tagType='red') < 3;
But this does not seem to be a valid statement. Help appreciated again; I might "reaccept" the answer.
Do LEFT JOIN rather than INNER JOIN :
SELECT i.id AS itemID
FROM Items i LEFT JOIN
Associations a
ON a.itemID = i.id
GROUP BY i.id
HAVING COUNT(a.tagID) < 3;
You can count in a subquery:
select *
from items i
where 2 >
(
select count(*)
from associations a
where a.itemID = i.id
);
This gets you all items with less than 2 associations (i.e. zero or one).

Query returned with an extra column in sql -ms access

So I am wondering. I fell into an interesting suggestion from another developer. So i basically have two tables I join in a query and I want the resulting table from the query to have an extra column that comes from the table on from the joint.
Example:
#table A: contains rating of players, changes randomly at any date depending
#on drop of form from the players
PID| Rating | DateChange |
1 | 2 | 10-May-2014 |
1 | 4 | 20-May-2015 |
1 | 20 | 1-June-2015 |
2 | 4 | 1-April-2014|
3 | 4 | 5-April-2014|
2 | 3 | 3-May-2015 |
#Table B: contains match sheets. Every player has a different match sheet
#and plays different dates.
MsID | PID | MatchDate | Win |
1 | 2 | 10-May-2014 | No |
2 | 1 | 15-May-2015 | Yes |
3 | 3 | 10-Apr-2014 | No |
4 | 1 | 21-Apr-2015 | Yes |
5 | 1 | 3-June-2015 | Yes |
6 | 2 | 5-May-2015 | No |
#I am trying to achieve this by running the ms-access query: i want to get
#every players rating at the time the match was played not his current
#rating.
MsID | PID | MatchDate | Rating |
1 | 2 | 10-May-2014 | 4 |
2 | 1 | 15-May-2015 | 2 |
3 | 3 | 10-Apr-2014 | 4 |
4 | 1 | 21-Apr-2015 | 4 |
5 | 1 | 3-June-2015 | 20 |
6 | 2 | 5-May-2015 | 3 |
This is what I have tried below:
Select MsID, PID, MatchDate, A-table.rating as Rating from B-table
left Join A-table
on B-table.PID = A-table.PID
where B-table.MatchDate > A-table.Datechange;
any help is appreciated. The solution can be in Vba as long as it returns something like a view/table I can manipulate using other queries or report.
Think of this in terms of sets of data... you need a set that lists the MAX dateChange for each player's and match date.
Soo...
SELECT MAX(A.DateChange) MDC, A.PID, B.Matchdate
FROM B-table B
INNER Join A-table A
on B.PID = A.PID
and A.DateChange <= B.MatchDate
GROUP BY A.PID, B.Matchdate
Now we take this and join it back to what you've done to limit the results in table A and B to ONLY those with that date player and matchDate (my inline table C)
SELECT B.MsID, B.PID, B.MatchDate, A.rating as Rating
FROM [B-table] B
INNER JOIN [A-table] A
on B.PID = A.PID
INNER JOIN (
SELECT MAX(Y.DateChange) MDC, Y.PID, Z.Matchdate
FROM [B-table] Z
INNER Join [A-table] Y
on Z.PID = Y.PID
and Y.DateChange <= Z.MatchDate
GROUP BY Y.PID, Z.Matchdate) C
on C.mdc = A.DateChange
and A.PID = C.PId
and B.MatchDate = C.Matchdate
I didn't create a sample for this using your data so it's untested but I believe the logic is sound...
Now Tested! SQL Fiddle using SQL server though...
My results don't match yours exactly. I think you're expected results are wrong though for MSID 4 given rules defined.

Postgres: Joining twice on a table

I (admitted SQL noob) have three tables in Postgresql that look like this:
groups
id | name | cat_id
----+--------+--------
1 | group1 | 1
3 | group3 | 1
2 | group2 | 2
4 | group4 | 2
category
id | name
----+------
1 | cat1
2 | cat2
translation
id | source | value | type | res_id
----+--------+---------+----------+--------
1 | group1 | Gruppe1 | groups | 1
2 | group2 | Gruppe2 | groups | 2
3 | group3 | Gruppe3 | groups | 3
4 | group4 | Gruppe4 | groups | 4
5 | cat1 | Kat1 | category | 1
6 | cat2 | Kat2 | category | 2
The translation table is global to the application and references other tables using the "res_id" and "type" fields. So to get the translation for "group1" I need to use "where res_id = 1 and type = 'groups'.
I need to list the groups in the following format:
category | group | translated category | translated group
This query gets me almost there:
select category.name, groups.name, translation.value from groups
join category on groups.cat_id = category.id
join translation on groups.id = translation.res_id
where type = 'groups';
But of course I'm missing the translated category, and have no clue how to get it.
I think you want something like:
select
category.name,
groups.name,
tg.value AS translated_group,
tc.value AS translated_category
from groups
inner join translation tg on (groups.id = tg.res_id AND tg.type = "groups")
inner join category on groups.cat_id = category.id
inner join translation tc on (category.id = tc.res_id AND tc.type = "category");
i.e. join translation twice, alias each copy, and use a join condition that also filters for the type field.
Untested as there's no CREATE TABLE and INSERT-form sample data in the question.
None of this is PostgreSQL specific, it's all just standard SQL.
BTW, it's nicer if you don't mix plural and singular forms for table names.

Oracle ordering by several same meaning columns

I have to make sortable table like this:
Sortable table:
building_id | building_age | title |
-------------------------------------------------
1 | 100 | New york buil |
2 | 50 | House 1 |
3 | 50 | House 10 |
From these tables:
Building Table:
building_id | building_age | building_type_1_FK | building_type_2_FK
---------------------------------------------------------
1 | 100 | null | 1
2 | 50 | 1 | null
3 | 50 | 2 | null
building_type_1:
type_id | title | diff1 |
-------------------------------------------------
1 | New york buil| blablabla |
building_type_2:
building_id | title |
----------------------------
1 | House 1 |
2 | House 10 |
3 | House 500 |
While joining these tables I get several title columns where one of them is not null. Is there any way to sort by title and select top 10 results without fetching all the data and then sorting in the app?
p.s.. I know that in general this architecture is not good, but I can't change it.
Yes. You want to do a left outer join to the two tables, and then bring the results together:
select b.building_id, b.building_age, coalesce(bt1.title, bt2.title) as title
from building b left outer join
building_type_1 bt1
on b.building_type_1_FK = bt1.type_id left outer join
building_type_2 bt2
on b.building_type_2_FK = bt2.building_id;
To get the top 10 results in Oracle:
select *
from (select b.building_id, b.building_age, coalesce(bt1.title, bt2.title) as title
from building b left outer join
building_type_1 bt1
on b.building_type_1_FK = bt1.type_id left outer join
building_type_2 bt2
on b.building_type_2_FK = bt2.building_id
order by title
) b
where rownum <= 10;