sql presto query to join 2 tables interatably - mysql

I need to do this in the sql query. LEt me know if this possible
I have a table which has a mapping like (table1)
num,value
2,'h'
3,'b'
5,'c'
Now I have another table which have these values (table2)
name, config
"test1",45
"test2",20
Now what I want is the sql query which will add another column to my table2 by checking if config column values are divisible by table1.num and if yes concat the table1.values to it
so now after the sql query it should become
name, config, final
"test1",45, bc
"test2",20, hc
Please let me know if I can form a query for this

You can by using a cross join,the mod function https://dev.mysql.com/doc/refman/8.0/en/mathematical-functions.html#function_mod and group_concat https://dev.mysql.com/doc/refman/8.0/en/group-by-functions.html#function_group-concat
select t2.name,t2.config,group_concat(t1.value separator '') final
from table1 t1
cross join table2 t2
where t2.config % t1.num = 0
group by t2.name,t2.config
+-------+--------+-------+
| name | config | final |
+-------+--------+-------+
| test1 | 45 | bc |
| test2 | 20 | hc |
+-------+--------+-------+
2 rows in set (0.00 sec)

The answer from P.Salmon should work for MySQL. If you are using Presto, then this would work:
SELECT t2.name,
t2.config,
array_join(array_agg(t1.value),'','') AS final
FROM table1 t1
CROSS JOIN table2 t2
WHERE t2.config % t1.num = 0
GROUP BY t2.name,
t2.config

Related

SQL Query with two tables and need to search one of the tables for specific and multiple column values

I need to write a complex query that involves the two sample tables below and I’m struggling to understand how to construct the query properly.
The table structure is as follows, along with sample data:
Table 1:
ID | Type | Size
A123 | Block | Medium
C368 | Square | Large
X634 | Triangle | Small
K623 | Square | Small
Table 2:
ID | Code | Description | Price
A123 | C06 | Sensitive Material | 99.99
A123 | H66 | Heavy Grade | 12.76
A123 | U74 | Pink Hue | 299.99
C368 | H66 | Heavy Grade | 12.76
C368 | G66 | Green Hue | 499.99
C368 | C06 | Sensitive Material | 99.99
C368 | K79 | Clear Glass | 59.99
X634 | G66 | Green Hue | 499.99
X634 | K79 | Clear Glass | 59.99
X634 | Z63 | Enterprise Class | 999.99
K623 | K79 | Clear Glass | 59.99
K623 | G66 | Green Hue | 499.99
K623 | X57 | Extra Piping | 199.99
The query should be based on the Type column from Table 1 primarily and then join on the ID column of Table 2. The goal of the query is to search for all IDs in Table 1 that have specific Code column combinations in Table 2.
The final output should be a table that looks like this for Type = Square AND both (Code = G66 AND Code = K79) as well:
ID
C368
K623
Those two IDs should be returned because they both have BOTH option codes in the pseudo query above.
How can I assemble this result using these two tables? Below are two initial queries I've written - neither produce the correct result. I've tried the IN operator along with =/AND/OR operators as you can see.
Attempt 1 (seems to work with ONE code but not > 1 code):
select distinct ID
from
(
SELECT shapes.ID, details.code, details.description
FROM db.table2 details
JOIN db.table1 shapes
ON details.VIN = shapes.VIN
WHERE shape.type='Square'
) src
where code IN ("G66", "K79")
-- where code = "G66" AND code = "K79" (Produces zero results)
-- where code = "G66" OR code = "K79" (Produces incorrect results)
Attempt 2 (seems to work with ONE code but not > 1 code):
SELECT distinct ID
FROM db.table2 details
WHERE ID IN
(
SELECT ID FROM db.table1 shapes
WHERE shapes.type='Square'
) AND code IN ("G66", "K79")
-- AND code = "G66" AND code = "K79" (produces zero results)
-- AND code = "G66" OR code = "K79" (Produces incorrect results)
Thanks
Whenever you have a problem that is of the ilk "I need the IDs from this table where there is one row that has value A and another row that has value B and both rows have this same ID" you need to select all the rows matching your criteria, group them and then count them and only use the rows that have the matching count:
SELECT t2.id
FROM table1 t1 JOIN table2 t2 ON t1.id = t2.id
WHERE t1.type = 'square' and t2.code IN ('G66', 'K79')
GROUP BY t2.id
HAVING COUNT(*) = 2
If there might be some bogus results like two rows that are G66 and no rows that are K79, then this simple counting will be defeated. We can instead look at the values (if it's 2) using MIN and MAX:
SELECT t2.id
FROM table1 t1 JOIN table2 t2 ON t1.id = t2.id
WHERE t1.type = 'square' and t2.code IN ('G66', 'K79')
GROUP BY t2.id
HAVING MIN(t2.code) = 'G66' AND MAX(t2.code) = 'K79'
It works because alphabetically G66 is less than K79, so G66 will be the min one
If we have 3 values that we must mandate, we can do some trick like turning all the codes into a number, and demanding the sum be something. I'll use base 2 for this:
SELECT t2.id
FROM table1 t1 JOIN table2 t2 ON t1.id = t2.id
WHERE t1.type = 'square' and t2.code IN ('G66', 'K79', 'X99')
GROUP BY t2.id
HAVING SUM(CASE t2.Code WHEN 'G66' THEN 1 WHEN 'K79' THEN 2 WHEN 'X99' THEN 4 END) = 7
If we map them to 1, 2 and 4 then the only way to make 7 (if the values are unique) is to have one of each. If there could be 7 G66 and none of the others, giving a bogus result, then we might have to count them individually:
SELECT t2.id
FROM table1 t1 JOIN table2 t2 ON t1.id = t2.id
WHERE t1.type = 'square' and t2.code IN ('G66', 'K79', 'X99')
GROUP BY t2.id
HAVING
SUM(CASE t2.code WHEN 'G66' THEN 1 ELSE 0 END) = 1 AND
SUM(CASE t2.code WHEN 'K79' THEN 1 ELSE 0 END) = 1 AND
SUM(CASE t2.code WHEN 'X99' THEN 1 ELSE 0 END) = 1
Here is what I think.
Filter data from t1 by the where clause, then join the t2 , 2 times, each to be filtered by conditions to have G66 & K79 in same table (2 different joins)
Select t1.ID
from t1
inner join t2 as t2_G66 on t1.ID = t2_G66.ID
inner join t2 as t2_K79 on t1.ID = t2_K79.ID
where t1.Type = 'Square' and
t2_G66.Code = 'G66' and
t2_K79.Code = 'K79'
Here is the fiddle
Couldn't you just use an INNER JOIN on the id's from Table 1 after getting the relevant rows which have the type we're looking for since Table 1 and Table 2 are related by ID?
I imagine we could first do a query to to get all the rows which have the relevant type and then run an INNER JOIN to get the shared rows which have the ID we care about.
Finally, we could just group our results by their code column?
Maybe something like this could work?:
SELECT * FROM db.table1 WHERE db.table1.type = "whatever"
INNER JOIN db.table2 ON db.table1.id = db.table2.id
GROUP BY db.table2.code HAVING COUNT(*) >= 1
AND db.table2.code IN ("code_1, code_2, code_3")
I just started SQL so I hope this hopes!
P.S. I realized I didn't cover your condition for having the code be a member of the the subset of codes that you care about. So I think this may work.

MySQL merging rows with equivalent ID

I have an issue where a mistake resulted in a database table having both emails and GUIDs mixed in the ID column.
The table looks like this (simplified):
GUID | Value | Date
cf#a | 21 | 2016
mf#b | 42 | 2015
mf#b | 21 | 2016
1aXd | 3 | 2016
a7vf | 9 | 2015
Where for example user cf#a and user 1aXd are the same. The GUID - Email combinations are stored in another table. The emails are unique. Primary key is the ID(GUID) and the Date combined. My problem is, how do i update the table and merge the rows? the Value column of two merging rows should be summed.
So the example table assuming (cf#a -> 1aXd and mf#b -> 8bga and ui#q -> a7vf) would become:
GUID | Value | Date
1aXd | 24 | 2016 <-- merged, Value = 21+3
8bga | 42 | 2015 <-- converted to GUID
8bga | 21 | 2016 <-- converted to GUID
<-- one row removed (merged with the first one)
a7vf | 9 | 2015 <-- untouched
Thank you for any help!
I could do this in C# but i would rather learn how to do it with the MySQL Workbench
Use JOIN:
SELECT t1.Value + t2.Value
FROM t1
JOIN t2 USING (`GUID`)
If you want update values, you need something like this:
UPDATE t1
JOIN t2 USING (`GUID`)
SET t1.Value = t1.Value + t2.Value
Removing merged rows:
DELETE t2 FROM t2
JOIN t1 USING (`GUID`)
UPDATE
If has only one table.
Merge:
UPDATE t1
JOIN (
SELECT GUID, SUM(Value) as amount, COUNT(1) as cnt
FROM t1
GROUP BY `GUID`
HAVING cnt > 1
) t2 ON t2.GUID = t1.GUID
SET t1.Value = t2.amount;
Delete:
CREATE table t2 (
GUID integer,
Value integer,
Date integer
);
INSERT INTO t2 (GUID, Value, Date)
SELECT GUID, Value, MAX(Date) FROM t1 GUID, Value;
Result will be in t2.

How to resolve SQL id chain?

I have an MySQL DB with table like that:
| id | redirect |
+-------+----------+
| 1 | NULL |
| 2 | 3 |
| 3 | NULL |
| 4 | 5 |
| 5 | 6 |
| 6 | 8 |
| 7 | NULL |
| 8 | NULL |
+-------+----------+
I need to create query for recursive resolving redirects.
So I can get results:
1 1
2 3
3 3
4 8
5 8
6 8
7 7
8 8
Thanks
One approach is to get each "level" with a separate query.
To get the first level, we can test for a NULL in redirect_id column to identify a "terminating" node.
To get the second level, we can use a JOIN operation to match rows that have a redirect_id that match the id from a "terminating" row (identified previously).
The third level follows the same pattern, adding another JOIN operation to return rows that redirect to a level two row.
And so on.
For example:
SELECT t1.id AS start_id
, t1.id AS terminate_id
FROM mytable t1
WHERE t1.redirect_id IS NULL
UNION ALL
SELECT t2.id
, t1.id
FROM mytable t1
JOIN mytable t2 ON t2.redirect_id = t1.id
WHERE t1.redirect_id IS NULL
UNION ALL
SELECT t3.id
, t1.id
FROM mytable t1
JOIN mytable t2 ON t2.redirect_id = t1.id
JOIN mytable t3 ON t3.redirect_id = t2.id
WHERE t1.redirect_id IS NULL
UNION ALL
SELECT t4.id
, t1.id
FROM mytable t1
JOIN mytable t2 ON t2.redirect_id = t1.id
JOIN mytable t3 ON t3.redirect_id = t2.id
JOIN mytable t4 ON t4.redirect_id = t3.id
WHERE t1.redirect_id IS NULL
The limitation of this single-query UNION ALL approach is that it would need to be extended to a finite maximum number of levels. (This approach is not truly "recursive".)
If we needed a truly recursive approach, we could run each query separately, just adding an extra "level" for each run, following the same pattern. We'd know that we'd exhausted all possible paths when the result of a query returns no rows.
I've demonstrated the use of the UNION ALL operator to combine the results into a single set, using a single query. (Add an ORDER BY clause at the end of the statement if the order of the rows is important. It would also be easy to include a literal "level" column to the resultset, e.g. 1 AS level for the first SELECT, the 2 on the second query, etc. to identify how far a node was from the termination.
MySQL doesn't support an Oracle style CONNECT BY syntax (in Oracle, we COULD write a single query that would traverse this set and return the specified rows, an arbitrary number of levels.)
To get a truly "recursive" approach in MySQL would require multiple queries. (Note that MySQL can support "recursion" in stored procedure calls, if the server is configured to allow it.)

MySQL - How to display row value as column name using concat and group_concat

Table 1:
id | typeid | available|
0 | 1 | 12 |
0 | 2 | 44 |
Table 2:
typeid | typename |
1 | CL |
2 | ML |
I have a query using concat and group_concat:
select id,concat(group_concat(typename,available)) as types from table1
join table2 on table2.typeid=table1.typeid
I got the result as:
id | types |
0 | CL12,ML44 |
But I want to display it like this:
id | CL | ML |
0 | 12 | 44 |
Is there any way to split the group_concat result to columns heads?
I want dynamically fetch data from table2. Some user can add data to table2. So hard-coding typename is not possible.
You should use table pivoting. There is no PIVOT command in MySQL, so you can use this query -
SELECT
t1.id,
MAX(IF(t2.typename = 'CL', t1.available, NULL)) AS CL,
MAX(IF(t2.typename = 'ML', t1.available, NULL)) AS ML
FROM table1 t1
JOIN table2 t2
ON t1.typeid = t2.typeid
GROUP BY
t1.id;
MySQL pivot tables (transform rows to columns).
Use GROUP_CONCAT function instead of MAX, if multiple available values are possible.
It can be done only if you know all the typenames in advance -- otherwise, you'll need to find another way. In databases that support returning result sets from stored procedures, it could be done with a stored proc. But mysql doesn't support that.
If you know all the typenames, here's how you build the query:
SELECT
id,
SUM(IF(typename = 'CL', available, 0)) AS `CL`,
SUM(IF(typename = 'ML', available, 0)) AS `ML`
FROM table1 join table2 on table1.typeid = table2.typeid
GROUP BY id
Try this:
SELECT a.id, MAX(IF(b.typename = 'CL', a.available, 0)) CL,
MAX(IF(b.typename = 'ML', a.available, 0)) ML
FROM table1 a
INNER JOIN table2 b ON a.typeid=b.typeid
GROUP BY a.id;
Use SUM function if you want to sum of the data from available column for particualr type else use the same query as ii is.

Duplicates in Database, Help Edit My Query to Filter Them Out?

I have just finished my latest task of creating an RSS Feed using PHP to fetch data from a database.
I've only just noticed that a lot (if not all) of these items have duplicates and I was trying to work out how to only fetch one of each.
I had a thought that in my PHP loop I could only print out every second row to only have one of each set of duplicates but in some cases there are 3 or 4 of each article so somehow it must be achieved by the query.
Query:
SELECT *
FROM uk_newsreach_article t1
INNER JOIN uk_newsreach_article_photo t2
ON t1.id = t2.newsArticleID
INNER JOIN uk_newsreach_photo t3
ON t2.newsPhotoID = t3.id
ORDER BY t1.publishDate DESC;
Table Structures:
uk_newsreach_article
--------------------
id | headline | extract | text | publishDate | ...
uk_newsreach_article_photo
--------------------------
id | newsArticleID | newsPhotoID
uk_newsreach_photo
------------------
id | htmlAlt | URL | height | width | ...
For some reason or another there are lots of duplicates and the only thing truely unique amongst each set of data is the uk_newsreach_article_photo.id because even though uk_newsreach_article_photo.newsArticleID and uk_newsreach_article_photo.newsPhotoID are identical in a set of duplicates, all I need is one from each set, e.g.
Sample Data
id | newsArticleID | newsPhotoID
--------------------------------
2 | 800482746 | 7044521
10 | 800482746 | 7044521
19 | 800482746 | 7044521
29 | 800482746 | 7044521
39 | 800482746 | 7044521
53 | 800482746 | 7044521
67 | 800482746 | 7044521
I tried sticking a DISTINCT into the query along with specifying the actual columns I wanted but this didn't work.
As you have noticed, the DISTINCT operator will return every id. You could use a GROUP BYinstead.
You will have to make a decision about wich id you want to retain. In the example, I have used MINbut any aggregate function would do.
SQL Statement
SELECT MIN(t1.id), t2.newsArticleID, t2.newsPhotoID
FROM uk_newsreach_article t1
INNER JOIN uk_newsreach_article_photo t2
ON t1.id = t2.newsArticleID
INNER JOIN uk_newsreach_photo t3
ON t2.newsPhotoID = t3.id
GROUP BY t2.newsArticleID, t2.newsPhotoID
ORDER BY t1.publishDate DESC;
Disclaimer
Now while this would be an easy solution to your immediate problem, if you decide that duplicates should not happen, you really should consider redesigning your tables to prevent duplicates getting into your tables in the first place.
group by all your selected columns with HAVING COUNT(*) > 1 will eleminate all duplicates like this:
SELECT *
FROM uk_newsreach_article t1
INNER JOIN uk_newsreach_article_photo t2
ON t1.id = t2.newsArticleID
INNER JOIN uk_newsreach_photo t3
ON t2.newsPhotoID = t3.id
GROUP BY t1.id, t1.headline, t1.extract, t1.text, t1.publishDate,
t2.id, t2.newsArticleID, t2.newsPhotoID,
t3.id, t3.htmlAlt, t3.URL, t3.height, t3.width
HAVING COUNT(*) > 1
ORDER BY t1.publishDate DESC;