mysql Create named table from values in select - mysql

I have a table, System, with a bunch of fields including System.serial.
I have a list of serial numbers that I want to get the status of.
Simple enough:
Select * from System where System.serial in ('s1','s2', 'sn');
However the list of serial numbers also has serials NOT IN the System table.
Obviously they are not in the results.
I want the missing serials to show in the results also but with no data.
The best way I can think of doing this is to make a temporary table with one column, serial, and then left join System on it.
How can I do this without creating a temporary table?
Something like:
Select listOfSerials.serial, System.*
from (Select ('s1','s2', 'sn') as serial ) as ListOfSerials
left join System on System.serial = ListOfSerials.serial;
Thanks,
Ryan

You're on the right track with your solution of creating a virtual table with which to do LEFT JOIN against your real data.
You can create a derived table as a series of UNIONed SELECT statements that select literal values with no table reference.
SELECT listOfSerials.serial, System.*
FROM (
SELECT 's1' AS serial
UNION SELECT 's2'
UNION SELECT 'sn'
) AS ListOfSerials
LEFT JOIN System ON System.serial = ListOfSerials.serial;
You only need to define a column alias in the first SELECT in the UNION. The rest are required to use that column alias.

Creating a reference table to store the list of serials is probably your best option. That would allow you to write a query like:
SELECT r.serial reference_serial, s.serial system_serial
FROM reference_table r
LEFT JOIN system_table s ON s.serial = r.serial
With the LEFT JOIN, serials declared in the reference table but unavailable in the system table will have second column set to NULL.
A quick and dirty work around is to use UNIONed subqueries to emulate the reference table:
SELECT r.serial reference_serial, s.serial system_serial
FROM (
SELECT 'serial1' AS serial
UNION ALL SELECT 'serial2'
UNION ALL SELECT 'serial2'
...
) r
LEFT JOIN system_table s ON s.serial = r.serial

Related

Find the row that doesn't exists in the table

In my table, there are values for poster_display_no 1 and 3 but not 2. I want to fetch the poster_display_no that doesnt exists in the table. The below query is not working as expected. Any idea what is wrong in the above query?
select `poster_display_no` as missing_num
from `poster-judging-app`.poster_details
where `poster_display_no` not in (1,2,3)
This is kind of a hack, but you could build a derived table with the list of poster_display_nos that you want to chek for, left join with your table and filter on the missing ones:
select t.poster_display_no
from (
select 1 poster_display_no
union all select 2
union all select 3
) t
left join poster_details p on p.poster_display_no = t.poster_display_no
where p.poster_display_no is null
Another, more scalable option would be to create a separate referential table to store the list of poster_display_nos that you want to check for, and then bring that table directly to the query.

SQL - Counting references in multiple tables

Here is the scenario. I have a MySQL table called modules which contains one or more entries each identified by a unique string - the module ID (mid).
There are a few other tables (scripts,images,sets...) which contain objects each of which"belong" to one of the modules - identified by the column 'mid' in each table.
Prior to allowing a user to drop a module entry, I need to check that the operation will not leave any orphaned objects in any of the other tables. Here is an example to make this clearer
Table modules
mname mid
Mod1 abcd1234
Mod2 wxyz9876
Table scripts
sname mid
A abcd1234
B wxyz9876
Table images
iname mid
A abcd1234
Table sets
sname mid
One or more of the tables may contain no, or no matching, entries.
I have written and tested a spot of SQL to handle this.
SELECT COUNT(*) FROM `images` WHERE mid = 'abcd1234'
UNION
SELECT COUNT(*) FROM `sets` WHERE mid = 'abcd1234'
UNION
SELECT COUNT(*) FROM `scripts` WHERE mid = 'abcd1234'
which very obligingly returns 1 implying that the module is "in use" and cannot be dropped. However, my SQL skills are pretty basic. I would much appreciate anyone who could tell me if this is a safe way to do things.
Not really a good way.
The UNION without ALL removes duplicate results. That would give you 1 if you had 3 rows returning 1. UNION ALL will make it return 3 rows with the count for each table, even when they are duplicate. After that you SUM them and you get the final count.
You should do:
SELECT SUM(cnt) FROM (
SELECT COUNT(*) as cnt FROM `images` WHERE mid = 'abcd1234'
UNION ALL
SELECT COUNT(*) FROM `sets` WHERE mid = 'abcd1234'
UNION ALL
SELECT COUNT(*) FROM `scripts` WHERE mid = 'abcd1234'
) a
You could build something around the following concept, given that there is a one-to-many relation between modules and the other tables.
select mid
,count(scripts.sname) as scripts
,count(images.iname) as images
,count(sets.sname) as sets
from modules
left join images using(mid)
left join sets using(mid)
left join scripts using(mid)
where mid = 'abcd1234'
group
by mid;
You could for example add the count(..) together, or including a HAVING clause.

Mysql Complex SELECT

I've three table 'Hardware_model','Warehouse' and 'Brand' and tables are refernced together in this way:
Hardware_model <-> Warehouse
Hardware_model <-> Brand
Now I want execute the following query: select all 'warehouse' object that has a 'brand_id equal to '10'). Off course warehouse and brand are not joined so no foreign keys exists between them. I was trying something like:
SELECT *
FROM warehouses
where hardware_id = (SELECT *
FROM hardwares
where brand_id='10')
but it doesn't works!
where hardware_id is a 'warehouse table' field and brand_id is a 'hardware table' field.
Any suggestions?
This sounds like a simple multi-table join. You just need to do something along the lines of (I can only guess the table structure).
SELECT w.* FROM warehouses w
JOIN hardwares h ON w.hardware_id = h.hardware_id
JOIN brands b ON h.brand_id = b.brand_id
WHERE brand_id=10;
It doesn't work because your subquery returns more than one column, and possibly more than one row. You should be able to do this with a JOIN:
SELECT *
FROM warehouses
JOIN hardwares ON warehouses.hardware_id = hardwares.id
WHERE brand_id = '10'
hardwares.id should be replaced with whatever key warehouses.hardware_id references. Even if you haven't specified a FOREIGN KEY constraint, you can still join tables.
just guessing at your tables, but maybe
SELECT w1.*
FROM warehouses w1,
hardware h1
where w1.hardware_id = h1.hardware_id
and h1.brand_id=10
Do you have a hardware_id in your warehouse table? if not, where are you making the relation between your warehouse and what is in it?
Surpriased that doesn't fall over in a big heap should be something like
SELECT * FROM warehouses
where hardware_id in (SELECT hardware_id FROM hardwares where brand_id='10')

MySQL JOIN tables with WHERE clause

I need to gather posts from two mysql tables that have different columns and provide a WHERE clause to each set of tables. I appreciate the help, thanks in advance.
This is what I have tried...
SELECT
blabbing.id,
blabbing.mem_id,
blabbing.the_blab,
blabbing.blab_date,
blabbing.blab_type,
blabbing.device,
blabbing.fromid,
team_blabbing.team_id
FROM
blabbing
LEFT OUTER JOIN
team_blabbing
ON team_blabbing.id = blabbing.id
WHERE
team_id IN ($team_array) ||
mem_id='$id' ||
fromid='$logOptions_id'
ORDER BY
blab_date DESC
LIMIT 20
I know that this is messy, but i'll admit, I am no mysql veteran. I'm a beginner at best... Any suggestions?
You could put the where-clauses in subqueries:
select
*
from
(select * from ... where ...) as alias1 -- this is a subquery
left outer join
(select * from ... where ...) as alias2 -- this is also a subquery
on
....
order by
....
Note that you can't use subqueries like this in a view definition.
You could also combine the where-clauses, as in your example. Use table aliases to distinguish between columns of different tables (it's a good idea to use aliases even when you don't have to, just because it makes things easier to read). Example:
select
*
from
<table> as alias1
left outer join
<othertable> as alias2
on
....
where
alias1.id = ... and alias2.id = ... -- aliases distinguish between ids!!
order by
....
Two suggestions for you since a relative newbie in SQL. Use "aliases" for your tables to help reduce SuperLongTableNameReferencesForColumns, and always qualify the column names in a query. It can help your life go easier, and anyone AFTER you to better know which columns come from what table, especially if same column name in different tables. Prevents ambiguity in the query. Your left join, I think, from the sample, may be ambigous, but confirm the join of B.ID to TB.ID? Typically a "Team_ID" would appear once in a teams table, and each blabbing entry could have the "Team_ID" that such posting was from, in addition to its OWN "ID" for the blabbing table's unique key indicator.
SELECT
B.id,
B.mem_id,
B.the_blab,
B.blab_date,
B.blab_type,
B.device,
B.fromid,
TB.team_id
FROM
blabbing B
LEFT JOIN team_blabbing TB
ON B.ID = TB.ID
WHERE
TB.Team_ID IN ( you can't do a direct $team_array here )
OR B.mem_id = SomeParameter
OR b.FromID = AnotherParameter
ORDER BY
B.blab_date DESC
LIMIT 20
Where you were trying the $team_array, you would have to build out the full list as expected, such as
TB.Team_ID IN ( 1, 4, 18, 23, 58 )
Also, not logical "||" or, but SQL "OR"
EDIT -- per your comment
This could be done in a variety of ways, such as dynamic SQL building and executing, calling multiple times, once for each ID and merging the results, or additionally, by doing a join to yet another temp table that gets cleaned out say... daily.
If you have another table such as "TeamJoins", and it has say... 3 columns: a date, a sessionid and team_id, you could daily purge anything from a day old of queries, and/or keep clearing each time a new query by the same session ID (as it appears coming from PHP). Have two indexes, one on the date (to simplify any daily purging), and second on (sessionID, team_id) for the join.
Then, loop through to do inserts into the "TempJoins" table with the simple elements identified.
THEN, instead of a hard-coded list IN, you could change that part to
...
FROM
blabbing B
LEFT JOIN team_blabbing TB
ON B.ID = TB.ID
LEFT JOIN TeamJoins TJ
on TB.Team_ID = TJ.Team_ID
WHERE
TB.Team_ID IN NOT NULL
OR B.mem_id ... rest of query
What I ended up doing is;
I added an extra column to my blabbing table called team_id and set it to null as well as another field in my team_blabbing table called mem_id
Then I changed the insert script to also insert a value to the mem_id in team_blabbing.
After doing this I did a simple UNION ALL in the query:
SELECT
*
FROM
blabbing
WHERE
mem_id='$id' OR
fromid='$logOptions_id'
UNION ALL
SELECT
*
FROM
team_blabbing
WHERE
team_id
IN
($team_array)
ORDER BY
blab_date DESC
LIMIT 20
I am open to any thought on what I did. Try not to be too harsh though:) Thanks again for all the info.

Selecting multiple columns/fields in MySQL subquery

Basically, there is an attribute table and translation table - many translations for one attribute.
I need to select id and value from translation for each attribute in a specified language, even if there is no translation record in that language. Either I am missing some join technique or join (without involving language table) is not working here since the following do not return attributes with non-existing translations in the specified language.
select a.attribute, at.id, at.translation
from attribute a left join attributeTranslation at on a.id=at.attribute
where al.language=1;
So I am using subqueries like this, problem here is making two subqueries to the same table with the same parameters (feels like performance drain unless MySQL groups those, which I doubt since it makes you do many similar subqueries)
select attribute,
(select id from attributeTranslation where attribute=a.id and language=1),
(select translation from attributeTranslation where attribute=a.id and language=1),
from attribute a;
I would like to be able to get id and translation from one query, so I concat columns and get the id from string later, which is at least making single subquery but still not looking right.
select attribute,
(select concat(id,';',title)
from offerAttribute_language
where offerAttribute=a.id and _language=1
)
from offerAttribute a
So the question part.
Is there a way to get multiple columns from a single subquery or should I use two subqueries (MySQL is smart enough to group them?) or is joining the following way to go:
[[attribute to language] to translation] (joining 3 tables seems like a worse performance than subquery).
Yes, you can do this. The knack you need is the concept that there are two ways of getting tables out of the table server. One way is ..
FROM TABLE A
The other way is
FROM (SELECT col as name1, col2 as name2 FROM ...) B
Notice that the select clause and the parentheses around it are a table, a virtual table.
So, using your second code example (I am guessing at the columns you are hoping to retrieve here):
SELECT a.attr, b.id, b.trans, b.lang
FROM attribute a
JOIN (
SELECT at.id AS id, at.translation AS trans, at.language AS lang, a.attribute
FROM attributeTranslation at
) b ON (a.id = b.attribute AND b.lang = 1)
Notice that your real table attribute is the first table in this join, and that this virtual table I've called b is the second table.
This technique comes in especially handy when the virtual table is a summary table of some kind. e.g.
SELECT a.attr, b.id, b.trans, b.lang, c.langcount
FROM attribute a
JOIN (
SELECT at.id AS id, at.translation AS trans, at.language AS lang, at.attribute
FROM attributeTranslation at
) b ON (a.id = b.attribute AND b.lang = 1)
JOIN (
SELECT count(*) AS langcount, at.attribute
FROM attributeTranslation at
GROUP BY at.attribute
) c ON (a.id = c.attribute)
See how that goes? You've generated a virtual table c containing two columns, joined it to the other two, used one of the columns for the ON clause, and returned the other as a column in your result set.