Combining object and property - sql

I have 3 tables.
First table name: Objects
ID
Name
1
Ahmet
2
Hasan
Second table name: Properties
ID
Name
1
EyeColor
2
Height
Third table name: Data
ObjectID
PropertyID
Value
1
1
Blue
1
2
1.70
2
1
Green
2
2
1.90
Using the above three tables, I want to create the following list with SQL.
Name
EyeColor
Height
Ahmet
Blue
1.70
Hasan
Green
1.90
I tried this transmission as sample SQL
SELECT Distinct Objects.Name, (SELECT * FROM Datas where PropertyID = Datas.PropertyID )
FROM Datas
INNER JOIN Objects ON Datas.ObjectID = Objects.ID
INNER JOIN Properties ON Datas.PropertyID = Properties.ID
Can someone help me please?

Start from the table that has the rows you want in your final result i.e. Objects (although I would strongly recommend never using this as a user table name since its also a system table name).
Then join on the information you need for your other columns.
I highly recommend the use of table aliases to make your query easier to read and understand.
select [Name], E.[Value] EyeColor, H.[Value] Height
from dbo.[Objects] O
inner join dbo.[Data] H on H.ObjectID = O.id
and H.PropertyID = (select P.id from dbo.Properties P where P.[Name] = 'Height')
inner join dbo.[Data] E on E.ObjectID = O.id
and E.PropertyID = (select P.id from dbo.Properties P where P.[Name] = 'EyeColor');

I will help you to figure how to solve this problem for now but you need to see a SQL tutorials,
So first of all you need in the O/P you want to select both EyeColor and Height for the person so how can you figure out that this value is for the specific person you need to check for that.
It will be a query like this:
SELECT Objects.Name AS NAME, EyeColor.Value AS EyeColor, Height.Value AS Height
FROM Objects AS Obj
INNER JOIN Data AS EyeColor ON EyeColor.ObjectID = Obj.ID
AND EyeColor.PropertyID = 1
INNER JOIN Data AS Height ON Height.ObjectID = Obj.ID
AND Height.PropertyID = 2;
You need to test it and modify it.
Note that: there are a lot of other solution so always try to figure what you want well first and then start divide the problem and then try to start writing your query.
Check #Dale K solution too at the same question as it can help you a lot.

Related

How to find an objects that doesn't have some required properties from dictionary

I have three tables:
object
id
Name
1
ball
2
pencil
object_properties
object_id
property_id
image
1
4
path
1
5
path
1
6
path
2
5
path
property
id
name
4
left
5
right
6
top
All rows in the table property are required for object_properties.
In this case query should find the second object pencil, because it doesn't have the all properties.
I tried a query:
select b.*
from objects b
left join object_properties p ON b.id = p.object_id
where property_id not in (select id from property)
But it's not working.
SQL Fiddle
Expected result:
id
Name
2
pencil
An alternative to Gordon's query is to not only compare the number of records, but the records themselves:
WITH j AS (
SELECT b.id,array_agg(p.property_id) prop
FROM objects b
LEFT JOIN object_properties p ON b.id = p.object_id
GROUP BY b.id
)
SELECT * FROM j
WHERE prop <> (SELECT array_agg(id) FROM property);
If you just count the records, the query will filter out objects that e.g. contain left three times, which is most certainly invalid.
Demo: db<>fiddle
You can use aggregation to count the properties on each property and then having to filter down to the ones missing properties:
select o.id, o.name
from objects o left join
object_properties op
on o.id = op.object_id left join
properties p
on op.property_id = p.id
group by o.id, o.name
having count(p.id) <> (select count(*) from properties);
Here is a db<>fiddle. Note that this returns both objets because the properties table has four rows not three.

adding and sorting a column in sql

I am trying to combine two tables in sql and am having trouble.
table g looks like this
id name
*************************
1234 john
3456 sarah
7890 ben
table f looks like this
id count
**************************
3456 2
1234 7
7890 5
I would like to insert a column into table g equal to the count in table f, sorted according to ID.
I've tried using the INNER JOIN and UNION commands, but neither of them seem to have worked. Does anyone know a solution? it seems like a pretty common problem.
SELECT g.id, g.name, f.count
FROM f,g
WHERE f.id = g.id
ORDER BY g.id;
The above is a simple join query.
In simple join queries, you select the information you want from both tables and set the where condition to be equal to the rows you want to join.
Also, you probably should set f.id and g.id as primary keys and set f.id as a foreign key reference to g.id.
I guess you want a select like this:
select g.id,g.name,f.count
from go
join f on g.id=f.id
order by g.id
but if you really want to phisicaly add a column(count) to table g and update it as mentioned in question you can do this:
alter table g
add column count int
and update the tale g like this:
update table g
set g.count=f.count
from g
join f on g.id=f.id

SQL joining to the same column id

I have a simple SQL query I just get to work out right. I've put together a test database like this;
TABLE MAIN
id name groupone grouptwo
1 Fred 1 3
2 bob 2 1
TABLE DETAIL
id group groupname
1 1 onegrp
2 2 twogrp
4 3 threegrp
My Select query is;
SELECT name, groupone, grouptwo, groupname
FROM main
INNER JOIN detail
ON main.groupone = detail.group
WHERE main.id = 1
The result I get is;
id name groupone grouptwo groupname
1 fred 1 3 onegrp
How do I change this to instead of giving the result as 1 and 3.
I get ... fred onegrp, threegrp
I've tried a dozen things but can't get it to work, I sort of want a give me the groupname again option but not sure what the syntax is for that! :(
Thanks in advance for your time and help
I think this is what you are after. You need to join the detail table twice, on the two different keys.
SELECT
m.Name
,a.groupname as 'GroupOne'
,b.groupname as 'GroupTwo'
FROM
main m
INNER JOIN
detail a
on m.groupone = a.group
INNER JOIN
detail b
on m.grouptwo = b.group
WHERE
m.id = 1

help with simple SQL update + join

I think this should be pretty simple, but I'm a SQL newb.
I have two tables. One is a list of items IDs and descriptions, the other is a map of corresponding old and new IDs. Like this:
ID_MAP
OLD_ID NEW_ID
---------------
1 101
2 102
ITEMS
ID DESCRIPTION
--------------------
1 "ItemA"
2 "ItemB"
...
101 <null>
102 <null>
I need to copy the old item descriptions to the new items according to the map. I think I need to use an inner join inside of an update, but it's not working and I'm not even sure that's the right way to go.
I'm trying statements like
update ITEMS
set (select ITEMS.DESCRIPTION
from ITEMS
join ID_MAP
on ITEMS.ID = ID_MAP.NEW_ID) =
(select ITEMS.DESCRIPTION
from ITEMS
join ID_MAP
on ITEMS.ID = ID_MAP.OLD_ID)
But of course it's not working. What should I be doing?
update new_item
set description = old_item.description
from items old_item
inner join id_map im
on old_item.id = im.old_id
inner join items new_item
on im.new_id = new_item.id
Depending on if UPDATE .. FROM is available in your DBMS (SQL Server vs Oracle) one possibility is using correlated sub-queries for each of your column updates. Not as convenient if you are able to do UPDATE FROM.
UPDATE items i
SET i.id = (
SELECT new_id
FROM id_map
WHERE old_id = i.id
)
, description = (
SELECT description
FROM id_map
WHERE old_id = i.id
)
You could add the following to the end too
WHERE EXISTS (
SELECT 1
FROM id_map
WHERE old_id = id
)

Get latest record from second table left joined to first table

I have a candidate table say candidates having only id field and i left joined profiles table to it. Table profiles has 2 fields namely, candidate_id & name.
e.g. Table candidates:
id
----
1
2
and Table profiles:
candidate_id name
----------------------------
1 Foobar
1 Foobar2
2 Foobar3
i want the latest name of a candidate in a single query which is given below:
SELECT C.id, P.name
FROM candidates C
LEFT JOIN profiles P ON P.candidate_id = C.id
GROUP BY C.id
ORDER BY P.name;
But this query returns:
1 Foobar
2 Foobar3
...Instead of:
1 Foobar2
2 Foobar3
The problem is that your PROFILES table doesn't provide a reliable means of figuring out what the latest name value is. There are two options for the PROFILES table:
Add a datetime column IE: created_date
Define an auto_increment column
The first option is the best - it's explicit, meaning the use of the column is absolutely obvious, and handles backdated entries better.
ALTER TABLE PROFILES ADD COLUMN created_date DATETIME
If you want the value to default to the current date & time when inserting a record if no value is provided, tack the following on to the end:
DEFAULT CURRENT_TIMESTAMP
With that in place, you'd use the following to get your desired result:
SELECT c.id,
p.name
FROM CANDIDATES c
LEFT JOIN PROFILES p ON p.candidate_id = c.id
JOIN (SELECT x.candidate_id,
MAX(x.created_date) AS max_date
FROM PROFILES x
GROUP BY x.candidate_id) y ON y.candidate_id = p.candidate_id
AND y.max_date = p.created_date
GROUP BY c.id
ORDER BY p.name
Use a subquery:
SELECT C.id, (SELECT P.name FROM profiles P WHERE P.candidate_id = C.id ORDER BY P.name LIMIT 1);