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
)
Related
I have a standard many-to-many schema like this:
items table:
id
name
1
foo
groups table:
id
name
slug
1
baz
qux
items_to_groups:
item_id
group_id
1
1
The groups.slug is used to query Group information. The ID is internal.
I need to query for items that are not in a specified group, but also need to include the group attributes (like name) in the result set.
The query is trivial without the need for values from the groups table, but I cannot figure out how to include them.
Here is my best attempt:
SELECT
g.slug,
g.name,
i.name
FROM
items AS i
LEFT JOIN items_to_groups AS itg ON i.id = itg.item_id
LEFT JOIN groups AS g ON itg.group_id = g.id
AND g.slug = 'group-slug-1'
WHERE
itg.item_id IS NULL;
Results:
||Item name 1
||Item name 2
Desired results:
group-slug-1|1|Item name 1
group-slug-1|1|Item name 2
Hmmm . . . I'm thinking:
select i.*, g.*
from items i cross join
groups g
where g.slug = 'group-slug-1' and
not exists (select 1
from items_to_groups ig
where ig.item_id = i.id and ig.group_id = g.id
);
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.
I have Products table with (Name, ParentID, Order) columns. I have a insert statement which keep the inserted child products. After the insert I need to update the order,
I have the following SQL,
UPDATE Products
SET [Order] = (SELECT ISNULL(MAX([Order]), 0) + 1 FROM Products WHERE ParentID = CP.ParentID)
FROM Products P
INNER JOIN #InsertedChildProduct CP ON (CP.ID = P.ID)
The problem is that I am updating the order of products that are just inserted, but [Order] is not working. If I have,
Products
--------
ParentID Order
----------------
1 1
1 2
and let say I have inserted 2 child products then the table should be,
Products
--------
ParentID Order
----------------
1 1
1 2
1 3
1 4
But I am seeing,
Products
--------
ParentID Order
----------------
1 1
1 2
1 3
1 3
You can try this, taken from the answers on here:
declare #MaxNumber int
set #MaxNumber = 0
UPDATE Products
SET [Order] = #MaxNumber, #MaxNumber = (SELECT ISNULL(MAX([Order]), 0)
FROM Products
WHERE ParentID = CP.ParentID) + 1
FROM Products P
INNER JOIN #InsertedChildProduct CP ON (CP.ID = P.ID)
Try like this instead, you need to gt the MAX() first in inner query
UPDATE P
SET [Order] = X.newval
FROM Products P
JOIN
(
SELECT ID, (ISNULL(MAX([Order]), 0) + 1) as newval
FROM Products P
JOIN #InsertedChildProduct ip
on P.ParentID = ip.ParentID
group by ID
) X
ON X.ID = P.ID
Thought about this a bunch of different ways and can't see how this would work without imposing order on both datasets, even if arbitrary. Here's one way to do it (Fiddle - make sure to build the schema first, then run the code): http://www.sqlfiddle.com/#!3/d34df/3)
WITH cteRN_c
AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID) AS RN_c,
ID
FROM #InsertedChildProduct
),
cteRN_p
AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY ParentID ORDER BY [Order]) AS RN_p,
ParentID,
[Order]
FROM Products
WHERE [Order] IS NULL
)
UPDATE p
SET [Order] = (SELECT ISNULL(MAX([ORDER]), 0) FROM Products WHERE ParentID = p.ParentID) + c.RN_c
FROM cteRN_p p INNER JOIN cteRN_c c
ON p.ParentID = c.ID AND
p.RN_p = c.RN_c;
We impose order by adding arbitrary row numbers to both the temp table set and the parent set, via ROW_NUMBER in CTEs. From that point, it's just a matter of joining the CTEs on the correct datapoints, and running the updates against the parent CTE. Granted, it's arbitrary which child will get numbered in which order, but at least it will happen.
Edit: Forgot the ISNULL in the MAX portion of the query - in case no children yet. Fiddle updated as well.
I have three tables (at least, something similar) with the following relationships:
Item table:
ID | Val
---------+---------
1 | 12
2 | 5
3 | 22
Group table:
ID | Parent | Range
---------+---------+---------
1 | NULL | [10-30]
2 | 1 | [20-25]
3 | NULL | [0-15]
GroupToItem table:
GroupID | ItemID
---------+---------
1 | 1
1 | 3
And now I want to add rows to the GroupToItem table for Groups 2 and 3, using the same query (since some other conditions not shown here are more complicated). I want to restrict the items through which I search if the new group has a parent, but to look through all items if there is not.
At the moment I am using an IF/ELSE on two statements that are almost exactly the same, but for the addition of another JOIN row when a parent exists. Is it possible to do a join to reduce the number of items to look at, only if a restriction is possible?
My two queries as they stand are given below:
DECLARE #GroupID INT = 2;...
INSERT INTO GroupToItem(GroupID, ItemID)
SELECT g.ID,
i.ID,
FROM Group g
JOIN Item i ON i.Val IN g.Range
JOIN GroupToItem gti ON g.Parent = gti.GroupID AND i.ID = gti.ItemID
WHERE g.ID = #GroupID
-
DECLARE #GroupID INT = 3;...
INSERT INTO GroupToItem(GroupID, ItemID)
SELECT g.ID,
i.ID,
FROM Group g
JOIN Item i ON i.Val IN g.Range
WHERE g.ID = #GroupID
So essentially I only want to do the second JOIN if the given group has a parent. Is this possible in a single query? It is important that the number of items that are compared against the range is as small as possible, since for me this is an intensive operation.
EDIT: This seems to have solved it in this test setup, similar to what was suggested by Denis Valeev. I'll accept if I can get it to work with my live data. I've been having some weird issues - potentially more questions coming up.
SELECT g.Id,
i.Id
FROM Group g
JOIN Item i ON (i.Val > g.Start AND i.Val < g.End)
WHERE g.Id = 2
AND (
(g.ParentId IS NULL)
OR
(EXISTS(SELECT 1 FROM GroupToItem gti WHERE g.ParentId = gti.GroupId AND i.Id = gti.ItemId))
)
SQL Fiddle
Try this:
INSERT INTO GroupToItem(GroupID, ItemID)
SELECT g.ID,
i.ID,
FROM Group g
JOIN Item i ON i.Val IN g.Range
WHERE g.ID = #GroupID
and (g.ID in (3) or exists (select top 1 1 from GroupToItem gti where g.Parent = gti.GroupID AND i.ID = gti.ItemID))
If a Range column is a varchar datatype, you can try something like this:
INSERT INTO GROUPTOITEM (GROUPID, ITEMID)
SELECT A.ID, B.ID
FROM GROUP AS A
LEFT JOIN ITEM AS B
ON B.VAL BETWEEN CAST(SUBSTRING(SUBSTRING(A.RANGE,1,CHARINDEX('-',A.RANGE,1)-1),2,10) AS INT)
AND CAST(REPLACE(SUBSTRING(A.RANGE,CHARINDEX('-',A.RANGE,1)+1,10),']','') AS INT)
Without knowing the name of a table and columns, I want to query the database retrieve the table and column names and then query the given tables.
I have an Oracle database schema that is like the following:
Item table:
Item_id, Item_type,
=================
1 box
2 book
3 box
Book table:
Item_id, title, author
===========================
2 'C# Programer', 'Joe'
Box table:
Item_id, Size
=====================
1, 'Large'
3, 'X Large'
Column_mapping table
Item_type, column_name, display_order
=====================================
box, Size, 1
book, title, 1
book, author 2
Table_mapping table:
Item_type, Table_name
========================
box, Box
book, Book
I would like a SQL statement that would give something like the following results:
Item_id, Item_type column1 column2
====================================
1, box, 'Large', <null>
2, book, 'C# Programer', 'Joe'
3, box, 'X Large', <null>
When I tried the simplified query
select *
from
(select Table_name
from Table_mapping
where Item_type = 'box')
where
Item_id = 1;
I get an error that Item_id is invalid identifier
and if I try
select *
from
(select Table_name
from Table_mapping
where Item_type = 'box');
I just get
Table_name
===========
Box
I am not sure how to proceed.
One way is to join both table and then use a coalesce on the column that can contain data from either table
SELECT
i.Item_id,
i.Item_type,
COALESCE(b.title, bx.size) column1,
b.author column2
FROM
Item i
LEFT JOIN Book b
ON i.item_id = b.item_id
LEFT JOIN Box bx
ON i.item_id = bx.item_id
Depending on how large your datasets are you may want to add a filter on the join e.g.
LEFT JOIN Book b
ON i.item_id = b.item_id
and i.item_type = 'book'
LEFT JOIN Box bx
ON i.item_id = bx.item_id
and i.item_type = 'box'
See it work at this SQLFiddle
If you wanted to do something based on the data in table_mapping or column_mapping you'd need to use dynamic SQL
Basically it is two separate queries. One for boxes and one for books. You can use union to merge the result sets together.
select i.Item_id, i.Item_type, b.size, null
from Item i inner join Box b on i.Item_id=b.Item_id
where i.Item_type = "box"
UNION
select i.Item_id, i.Item_type, b.title, b.author
from Item i inner join Book b on i.Item_id=b.Item_id
where i.Item_type = "book"
ORACLE actually stores the table- and column names in its data dictionary, so there is no need for you to maintain those data separately. Try this to get the table names:
SELECT table_name FROM user_tables;
Then do this to get the columns for each table:
SELECT column_name FROM user_tab_columns WHERE table_name = 'MYTABLE';
Once you do that, you will need to create a stored procedure in order to execute dynamic SQL. I don't think you can do this in a plain-vanilla query.