I have a uuid that's stored in one table as a human-readable guid, but in another table it's split into upper and lower bits. How can I write a query to join the tables on the uuid?
Edit: table2 will only have 1 result with the given upper & lower bits, so hopefully efficiency shouldn't be too terrible, but please factor this into answers.
table1.uuid = 'b33ac8a9-ae45-4120-bb6e-7537e271808e'
table2.upper_bits = -5531888561172430560, table2.lower_bits = -4940882858296115058
I need to retrieve table2.status along with table1.* where table2.upper_bits + table2.lower_bits = table1.uuid (pseudo where statement) but I don't know how to either sum the table2 upper & lower values for the join, or how to convert table1's uuid to bits for the join.
Thanks!
Something like this might work... but would obviously be highly inefficient.
SELECT ...
FROM table1 AS t1
INNER JOIN table2 AS t2 ON REPLACE(t1.uuid, '-', '')
= CONCAT(HEX(t2.upper_bits), HEX(t2.lower_bits))
...
...you might to force upper/lower case depending on collation/comparison.
I'd kind of lean toward it being "absolutely necessary" to change your database structure (to respond to a comment you made on another answer). To minimize the impact on existing queries and logic, you could change one of the tables to have additional matching fields to the other, and add triggers to the table to automatically populate/update the new fields; and then do a one time update to set all the old records' values.
I'd try to go with modifying t1 first, since an index on two ints is likely "better" than on one string; but I'm not sure how straight-forward converting the string to the upper and lower bits would be.
Modifying t2 would be easier, the triggers would be little more than SET NEW.uuid = CONCAT(HEX(NEW.upper_bits), HEX(NEW.lower_bits)); ...I say "little more than" because it would be best for the trigger to also insert the -'s at the expected points as well so the join condition could eliminate all function use.
Edit: I found a way to calculate the bits in pure SQL:
SELECT #uuid := REPLACE('b33ac8a9-ae45-4120-bb6e-7537e271808e', '-', '') AS uuid
, -1 * CAST((~CAST(CONV(SUBSTRING(#uuid, 1, 16), 16, 10) AS SIGNED) + 1) AS SIGNED) AS upper_bits
, -1 * CAST((~CAST(CONV(SUBSTRING(#uuid, 17, 16), 16, 10) AS SIGNED) + 1) AS SIGNED) AS lower_bits
;
You may be able to use something like this in triggers for t1, and a one time update for t1 for the new fields.
...it might even help with the join:
ON -1 * CAST((~CAST(CONV(SUBSTRING(REPLACE(t1.uuid, '-', ''), 1, 16), 16, 10) AS SIGNED) + 1) AS SIGNED)
= t2.upper_bits
AND -1 * CAST((~CAST(CONV(SUBSTRING(REPLACE(t1.uuid, '-', ''), 17, 16), 16, 10) AS SIGNED) + 1) AS SIGNED)
= t2.lower_bits
Note: Yes, the excessive casting in both of these appears to be necessary (at least on the older version of MySQL I tested the calculation against.)
Here's a variation of #Uueerdo's solution that should be more efficient (sort of a decorate-join-undecorate), but I haven't run the EXPLAINs to know for sure:
SELECT t1.*, t2.status
FROM (
SELECT UUID_TO_BIN(uuid) AS tmpid, *
FROM table1
) AS t1 INNER JOIN (
SELECT UUID_TO_BIN(CONCAT(HEX(upper_bits), HEX(lower_bits))) AS tmpid, status
FROM table2
) AS t2 ON t1.tmpid = t2.tmpid
It might use a bit more memory, which is something to keep in mind if the tables have many rows and/or if table1 is very wide.
If you only need the records from table1 and table2 matching a single UUID, you should just execute two queries, not a join:
SELECT *
FROM table1
WHERE UUID_TO_BIN(uuid) = UUID_TO_BIN(?)
SELECT status
FROM table2
WHERE UUID_TO_BIN(CONCAT(HEX(upper_bits), HEX(lower_bits))) = UUID_TO_BIN(?)
If upper_bits and lower_bits are indexed, this would be a better way to query table2:
SET #tmpid = UUID_TO_BIN(?);
SELECT status
FROM table2
WHERE upper_bits = #tmpid >> 64, lower_bits = _binary X'FFFFFFFFFFFFFFFF' & #tmpid
And you could apply similar logic to my first solution (I think):
SELECT t1.*, t2.status
FROM (
SELECT
UUID_TO_BIN(uuid) >> 64 AS upper_bits,
_binary X'FFFFFFFFFFFFFFFF' & UUID_TO_BIN(uuid) AS lower_bits,
*
FROM table1
) AS t1 INNER JOIN (
SELECT upper_bits, lower_bits, status
FROM table2
) AS t2 ON t1.upper_bits = t2.upper_bits AND t1.lower_bits = t2.lower_bits
None of this is tested but hopefully it gives you some ideas to play around with.
The easiest way is to store lower_bits & upper_bits in table1 together with the uuid. Then join the tables on lower_bits & upper_bits.
Edit: If you need only a single row, and you are sure, that there will be a single matching row in the other table, then calculate the uuid= #uuid, lower_bits =#lbits & upper_bits= #ubits, and then run the following:
Select t1.*, t2.status
From
(select * from table1 where uuid = #uuid) as t1
Cross join
(select status from table2 where lower_bits =#lbits and upper_bits= #ubits) as t2;
Related
This SHOULD be trivial but I am going round in circles, perhaps someone can help.
I have two tables (T1, T2) from which I wish to extract a number of values in each row and update the contents of a third table (T3) iff (if and only if) two UQ, NN fields in T1, T2 match, in which case I want to add some of the values in the corresponding rows from T1, T2 together and put them in T3 and copy some other values over to T3.
The fields to be summed are all declared DECIMAL.
Simplified and in pseudocode (to avoid making too many assumptions):
SELECT T1.a,T1.b,T1.c from T1
SELECT T2.d, T2.e from T2
UPDATE T3.col1=a, T3.col2=b, T3.col3 = (value of(T2.c) + value of(T2.e)) iff T1.a = T2.d
A variety of attempts have failed to work.
I am running MySQL Workbench 5.2.37 on Ubuntu 12.10
Example from comment below:
UPDATE Test_join as T3
SELECT GZHident, Magnitude_1 from GZHTableExtended3 as T1
SELECT AHZid, DM from AHZDMConversionTable as T2 JOIN T2,T1
ON T1.GZHident = T2.AHZid
SET T3.AHZid = T1.GZHident
SET T3.DM = T2.DM
SET T3.Abs_Magnitude_1 = T1.Magnitude_1 + T2.DM;
MySQL supports multi-table UPDATE using JOIN syntax, just like SELECT. This isn't standard ANSI SQL, but it's very useful for these types of situations.
Example:
UPDATE T1
JOIN T2 ON T1.a = T2.d
JOIN T3 ON ...???...
SET T3.col1=T1.a,
T3.col2=T1.b,
T3.col3 = T2.c + T2.e;
You don't have enough information in your question for me to guess how to join to T3.
Thanks for posting an example UPDATE query. But it's still not clear how you want to update T3. The sample query you give is not valid SQL code at all. I suggest you read a tutorial on programming in SQL.
You seem be confused between UPDATE and INSERT? Keep in mind:
INSERT adds new rows.
UPDATE changes values in existing rows.
I'm guessing from your wording, but perhaps you want to add new rows. You can do that in this way:
INSERT INTO Test_join (AHZid, DM, Abs_Magnitude)
SELECT T1.GZHident, T2.DM, T1.Magnitude_1 + T2.DM
FROM GZHTableExtended3 as T1
JOIN AHZDMConversionTable as T2 JOIN T2
ON T1.GZHident = T2.AHZid;
I have a query where I have to join two tables. Lets say T1 and T2.
T1 has a column with an Id. And every row has only one Id value. However, the second table is where i'm struggling. in T2 there is a column with the Id's but it can be possible that one row has multiple of those Id's.
So as example T1.col1 has this value: 737382. But T2.col2 can have this entries in one row: 737382;239112;2838210;9923834;2388342;...
I know that this structure violates the 1NF and stuff. But i cannot change anything in the data or structure of the data.
now what i want to do is to join this two tables. Please let me know if i'm missing to tell any relevant information in order to answer my question. its late and my brain is exhausted ~.~
try
select tab2.* -- whatever
from t1 tab1
inner join t2 tab2 on ( ';'||tab2.col2||';' like '%;'||tab1.col1||';%' )
;
the extra affixed ; characters serve to avoid disjunctions in the join condition.
You could use regular expressions in your join, your regular expression can check for your T1.col1 in T2.col2. The regular expression should check for the value from the begining of the string (i.e. T2.col2) or being preceeded by ';' and always followed by ';'
Have you tried something like:
select
a.column1,
a.column2,
b.column1,
b.column2
from table a
inner join table b on a.column1 = b.column1
Since one T2.Col2 can hold n entries, you need to parse them to rows using a table-valued function and then using CROSS APPLY
BEWARE if the T2 is big this solution will hang for quite long time
something like this:
;WITH IDSplitted AS
(
SELECT *
FROM T2
CROSS APPLY dbo.[StringSplit](col2, ';')
)
SELECT * -- or whatever columns you need
FROM T1
INNER JOIN IDSplitted ON IDSplitted.val = t1.col1
having StringSplit:
CREATE FUNCTION [dbo].[StringSplit]
(
#delimited nvarchar(max),
#delimiter nvarchar(100)
) RETURNS #t TABLE
(
-- Id column can be commented out, not required for sql splitting string
id int identity(1,1), -- I use this column for numbering splitted parts
val nvarchar(max)
)
AS
BEGIN
declare #xml xml
set #xml = N'<root><r>' + replace(#delimited,#delimiter,'</r><r>') + '</r></root>'
insert into #t(val)
select
r.value('.','varchar(max)') as item
from #xml.nodes('//root/r') as records(r)
RETURN
END
After using the solution of collapstar (which was correct) i encountered performance issues. so what i did is to create a mapping table so that when i have to run the query again, i dont have to wait so long. so i have a scheduled job which does the join and writes the output into a mapping table over night
I have started using sql and have heard much about the ANY and ALL operators. Can somebody explain to me the kind of queries they are used in and how they work?
The ANY and ALL operators allow you to perform a comparison between a single column value and a range of other values. For instance:
select * from Table1 t1 where t1.Col1 < ANY(select value from Table2)
ANY means that the condition will be satisfied if the operation is true for any of the values in the range. ALL means that the condition will be satisfied only if the operation is true for all values in the range.
To use an example that might hit closer to home, doing this:
select * from Table1 t1 where t1.Col1 = ANY(select value from Table2)
Is the same as doing this:
select * from Table1 t1 where t1.Col1 in (select value from Table2)
I have heard much about the ANY and
ALL operators
I'm mildly surprised: I rarely see them used myself. Far more commonly seen are WHERE val IN (subquery) and WHERE EXISTS (subquery).
To borrow #Adam Robinson's example:
SELECT *
FROM Table1 AS t1
WHERE t1.Col1 < ANY (
SELECT value
FROM Table2
);
I more usually see this written like this:
SELECT *
FROM Table1 AS t1
WHERE EXISTS (
SELECT *
FROM Table2 AS t2
WHERE t1.Col1 < t2.value
);
I find this construct easier to read because the parameters of the predicate (t1.Col1 and t2.value respectively) are closer together.
Answers above addressed some aspects of "ANY" and did not address "ALL".
Both of these are more useful when comparing against another table and its entries are changing dynamically.
Especially true for < ANY and > ANY, since for static arguments, you could just take MAX/MIN respectively, and drop the "ANY".
For example, this query -
SELECT ProductName, ProductID FROM Products
WHERE ProductID > ANY (100, 200, 300);
can be simplified to -
SELECT ProductName, ProductID FROM Products
WHERE ProductID > 100;
Note that the "ALL" query will end up comparing one column value with ALL (...) which will always be false unless "ALL" arguments are identical.
For ex -
SELECT ProductName, ProductID FROM Products
WHERE ProductID = ALL (SELECT ProductID FROM OrderDetails);
which is always empty/ false when subquery is multi-valued like -
SELECT ProductName, ProductID FROM Products
WHERE ProductID = ALL (10, 20, 30);
Adding to Adam's reply, be wary that the syntax can be ambiguous:
SELECT b1 = ANY((SELECT b2 FROM t2 ...)) FROM t1 ...;
Here ANY can be considered either as introducing a subquery, or as being an aggregate function, if the subquery returns one row with a Boolean value. (via postgresql.org)
Sample query that may put some context into this. Let's say we have a database of major league baseball players and we have a database of common Puerto Rican last names. Let's say somebody wanted to see how common Puerto Rican players are on the MLB. They could run the following query:
SELECT mlb_roster.last_name FROM mlb_roster WHERE mlb_roster.last_name = ANY (SELECT common_pr_names.last_name FROM common_pr_names)
What the query is doing here is comparing the last names on the MLB roster and displaying only the ones that are also found on the list of common Puerto Rican names.
What I'm attempting to do find values in a table which are less than the MAX of another field, minus a numeric value. Eg:
...WHERE some_table.value_1 = 0 AND another_table.value_2 <= (SELECT MAX(another_table.value_3) - 5) ORDER BY...
However, this is not working! My joins are all fine, and the query runs without the 2nd part of the WHERE statement, but if you'd like to see the rest of the code for more info, let me know!
Cheers!
Sparkles*
ps all the values are integers
Here is a working example using joins, try to apply it to yours:
SELECT *
FROM table1 t1
INNER JOIN table2 t2
ON t1.join_field = t2.join_field
WHERE t1.some_field = 1
AND t2.other_field <= (
SELECT (MAX(t22.third_field) - 5)
FROM table2 t22
);
If this is not exactly what you were looking for, please let me know and I will update it.
Use HAVING MAX(...)
Something like:
SELECT MIN(p.price) AS price, p.pricegroup
FROM articles_prices AS p
_YOUR_JOINED_TABLE_
WHERE p.articleID=10
GROUP BY p.pricegroup
HAVING MAX(p.price) > _VALUE_FROM_JOINED_TABLE_;
SELECT *
FROM `seriallog` WHERE `lastevent` IN
(SELECT GROUP_CONCAT(CONCAT(' \'', `eventid`, '\''))
FROM `permissions` WHERE `permissions`.`userpkid` = 1)
You are likely trying to compare an int (lastevent) to a concatenated string (the result of your subquery).
It is hard to tell without more info, but this probably what you want to do:
select *
from seriallog sl
inner join permissions p on sl.lastevent = p.eventid
where p.userpkid = 1
The way you've written it, the inner query will return a string like "1, 2, 3" so only rows in seriallog with a lastevent column of "1, 2, 3" exactly will match.
You're presumably interested in matching any row whose value is in a SET. That's what SQL is designed to do; you don't have to apply any special engineering. The following would work:
SELECT *
FROM seriallog WHERE lastevent IN
(SELECT eventid FROM permissions WHERE permissions.userpkid = 1)
However, it would be preferable to instead write:
SELECT *
FROM seriallog
WHERE EXISTS (SELECT 1 FROM permissions WHERE eventid = seriallog.lastevent AND permissions.userpkid = 1)
This allows MySQL to treat the query more like a JOIN, and presumably execute it more efficiently.
This is most optimized format for MySQL. In general MySQL does not love Sub-Queries.
SELECT *
FROM seriallog sl
join permissions p on sl.lastevent=p.eventid
WHERE permissions.userpkid = 1