Sql Server how to find values in different tables that have different suffix - sql

I'm struggling to find a value that might be in different tables but using UNION is a pain as there are a lot of tables.
[Different table that contains the suffixes from the TestTable_]
| ID | Name|
| -------- | -----------|
| 1 | TestTable1 |
| 2 | TestTable2 |
| 3 | TestTable3 |
| 4 | TestTable4 |
TestTable1 content:
| id | Name | q1 | a1 |
| -------- | ---------------------------------------- |
| 1 | goose | withFeather? |featherID |
| 2 | rooster| withoutFeather?|shinyfeatherID |
| 3 | rooster| age | 20 |
TestTable2 content:
| id | Name | q1 | a1 |
| -------- | ---------------------------------------------------|
| 1 | brazilian_goose | withFeather? |featherID |
| 2 | annoying_rooster | withoutFeather?|shinyfeatherID |
| 3 | annoying_rooster | no_legs? |dead |
TestTable3 content:
| id | Name | q1 | a1 |
| -------- | ---------------------------------------- |
| 1 | goose | withFeather? |featherID |
| 2 | rooster| withoutFeather?|shinyfeatherID |
| 3 | rooster| age | 15 |
Common columns: q1 and a1
Is there a way to parse through all of them to lookup for a specific value without using UNION because some of them might have different columns?
Something like: check if "q1='age'" exists in all those tables (from 1 to 50)
Select q1,*
from (something)
where q1 exists in (TestTable_*)... or something like that.
If not possible, not a problem.

You could use dynamic SQL but something I do in situations like this where I have a list of tables that I want to quickly perform the same actions on is to either use a spreadsheet to paste the list of tables into and type a query into the cell with something like #table then use the substitute function to replace it.
Alternative I just paste the list into SSMS and use SHIFT+ALT+ArrowKey to select the column and start typing stuff out.
So here is my list of tables
Then I use that key combo. As you can see my cursor has now selected all those rows.
Now I can start typing and all rows selected will get the input.
Then I just go to the other side of the table names and repeat the action
It's not a perfect solution but it's quick a quick and dirty way of doing something repetitive quickly.
If you want to find all the tables with that column name you can use information schema.
Select table_name from INFORMATION_SCHEMA.COLUMNS where COLUMN_NAME = 'q1'

Given the type of solution you are after I can offer a method that I've had to use on legacy systems.
You can query sys.columns for the name of the column(s) you need to find in N tables and join using object_id to sys.tables where type='U'. This will give you a list of table names.
From this list you can then build a working query for each table, and depending on your requirements (is this ad-hoc?) either just manually execute it yourself of build a procedure that will do it for you using sp_executesql
Eg
select t.name, c.name
into #workingtable
from sys.columns c
join sys.tables t on t.object_id=c.object_id
where c.name in .....
psudocode:
begin loop while rows exist in #working table
select top 1 row from #workingtable
set #sql=your query specific to that table and column(s)
exec(#sql) / sp_executesql / try/catch as necessary
delete row from working table
end loop
Hopefully that give ideas at least for how you might implement your requirements.

Related

Filling information from same and other tables in SQL

For my further work I need to create a lookup table where all the different IDs my data has (because of different sources) are noted.
It has to look like this:
Lookup_Table:
| Name | ID_source1 | ID_source2 | ID_source3 |
-----------------------------------------------
| John | EMP_992 | AKK81239K | inv1000003 |
Note, that Name and ID_Source1 are coming from the same table. The other IDs are coming from different tables. They share the same name value, so e.g. source 2 looks like this:
Source2 Table:
| Name | ID |
--------------------
| John | AKK81239K |
What is the SQL code to accomplish this? Im using Access and it doesnt seem to work with this code for source 2:
INSERT INTO Lookup_Table ([ID_Source2])
SELECT [Source2].[ID]
FROM Lookup_Table LEFT JOIN [Source2]
ON [Lookup_Table].[Name] = [Source2].[Name]
It just adds the ID from Source2 in a new row:
| Name | ID_source1 | ID_source2 | ID_source3 |
-----------------------------------------------
| John | EMP_992 | | |
| | | AKK81239K | |
Hope you guys can help me.
You're looking for an UPDATE query, not an INSERT query.
An UPDATE query updates existing records. An INSERT query inserts new records into a table.
UPDATE Lookup_Table
INNER JOIN [Source2] ON [Lookup_Table].[Name] = [Source2].[Name]
SET [ID_Source2] = [Source2].[ID]

Need alternate SQL

I am currently working with an H2 database and I have written the following SQL, however the H2 database engine does not support the NOT IN being performed on a multiple column sub-query.
DELETE FROM AllowedParam_map
WHERE (AllowedParam_map.famid,AllowedParam_map.paramid) NOT IN (
SELECT famid,paramid
FROM macros
LEFT JOIN macrodata
ON macros.id != macrodata.macroid
ORDER BY famid)
Essentially I want to remove rows from allowedparam_map wherever it has the same combination of famid and paramid as the sub-query
Edit: To clarify, the sub-query is specifically trying to find famid/paramid combinations that are NOT present in macrodata, in an effort to weed out the allowedparam_map, hence the ON macros.id != macrodata.macroid. I'm also terrible at SQL so this might be completely the wrong way to do it.
Edit 2: Here is some more info about the pertinent schema:
Macros
| ID | NAME | FAMID |
| 0 | foo | 1 |
| 1 | bar | 1 |
| 2 | baz | 1 |
MacroData
| ID | MACROID | PARAMID | VALUE |
| 0 | 0 | 1 | 1024 |
| 1 | 0 | 2 | 200 |
| 2 | 0 | 3 | 89.85 |
AllowedParam_Map
| ID | FAMID | PARAMID |
| 0 | 1 | 1 |
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 1 | 4 |
The parameters are allowed on a per-family basis. Notice how the allowedParam_map table contains an entry for famid=1 and paramid=4, even though macro 0, aka "foo", does not have an entry for paramid=4. If we expand this, there might be another famid=1 macro that has paramid=4, but we cant be sure. I want to cull from the allowedParam_map table any unused parameters, based on the data in the macrodata table.
IN and NOT IN can always be replaced with EXISTS and NOT EXISTS.
Some points first:
You are using an ORDER BY in your subquery, which is of course superfluous.
You are outer-joining a table, which should have no effect when asking for existence. So either you need to look up a field in the outer-joined table, then inner-join it or you don't, then remove it from the query. (It's queer to join every non-related record (macros.id != macrodata.macroid) anyway.
You say in the comments section that both famid and paramid reside in table macros, so you can remove the outer join to macrodata from your query. You get:
As you say now that famid is in table macros and paramid is in table macrodata and you want to look up pairs that exist in AllowedParam_map, but not in the aformentioned tables, you seem to be looking for a simple inner join.
DELETE FROM AllowedParam_map
WHERE NOT EXISTS
(
SELECT *
FROM macros m
JOIN macrodata md ON md.macroid = m.id
WHERE m.famid = AllowedParam_map.famid
AND md.paramid = AllowedParam_map.paramid
);
You can use not exists instead:
DELETE FROM AllowedParam_map m
WHERE NOT EXISTS (SELECT 1
FROM macros LEFT JOIN
macrodata
ON macros.id <> macrodata.macroid -- I strongly suspect this should be =
WHERE m.famid = ?.famid and m.paramid = ?.paramid -- add the appropriate table aliases
);
Notes:
I strongly suspect the <> should be =. <> does not make sense in this context.
Replace the ? with the appropriate table alias.
NOT EXISTS is better than NOT IN anyway. It does what you expect if one of the value is NULL.

SQL multiple join statement issues

I'm trying to create a stored procedure that will retrieve a table (called CommsLog) and match it to a user table to return all the names that are associated with it.
The user database stores all the users by alias and then I am trying to look up their first and last name in the database, concatenate them and return that into my results
This is what I've got at the moment but it only returns part of the table and the same names for both columns (These should be different)
SELECT
CommsLog.ID,
CommsLog.CommType,
CommsLog.Date,
Users.FirstName +' ' + Users.LastName,
Users.FirstName +' ' + Users.LastName,
CommsLog.Version
FROM CommsLog
INNER JOIN Users
ON CommsLog.SentTo=Users.Alias and CommsLog.SentFrom=.Users.Alias
EDIT: Update with Data and output
CommsLog table looks like:
+----+----------+--------------------------+----------+----------+---------+
| ID | CommType | Date | SentTo | SentFrom | Version |
+----+----------+--------------------------+----------+----------+---------+
| 12 | Test | 2014-12-19 09:38:10.000 | uk\tmot | uk\gmab | 1.10 |
+----+----------+--------------------------+----------+----------+---------+
User table looks like:
+---------+-----------+----------+------------+----------------------------------+
| Alias | FirstName | LastName | Telephone | email |
+---------+-----------+----------+------------+----------------------------------+
| uk\tmot | Tom | motoll | 0731424523 | tom.motoll#stackoverflow.com |
| uk\gmab | Grant | maberick | 0756463345 | grant.maberick#stackoverflow.com |
+---------+-----------+----------+------------+----------------------------------+
Ouput:
+----+----------+-------------------------+------------+----------------+---------+
| ID | CommType | Date | SentTo | SentFrom | Version |
+----+----------+-------------------------+------------+----------------+---------+
| 12 | Test | 2014-12-19 09:38:10.000 | Tom motoll | Grant maberick | 1.10 |
+----+----------+-------------------------+------------+----------------+---------+
It looks to me like you're trying to pull in both the names of the SentTo users as well as the names of the SentFrom users in your SELECT statement. If that's the case, then you're actually going to need to join your USERS table into your query twice, with aliases -- once for your SentTo users and once for your SentFrom users. Try this for a query, instead:
SELECT
CSLL.CommsLog.ID,
CSLL.CommsLog.CommType,
CSLL.CommsLog.Date,
UserTo.FirstName +' ' + UserTo.LastName,
UserFrom.FirstName +' ' + UserFrom.LastName,
CSLL.CommsLog.Version
FROM CSLL.CommsLog
INNER JOIN CSLL.Users UserTo ON CSLL.CommsLog.SentTo=UserTo.Alias
INNER JOIN CSLL.Users UserFrom ON CSLL.CommsLog.SentFrom=UserFrom.Alias
Since you didn't post your table structure or any sample data, you may need to tweak that query a bit to make it work, but it should at least get you close to what you're after.
Are you meaning to only get records that were sent from a user to themselves?
That is what your join condition is doing.

SQL - Selecting all latest unique records

I'm struggling a bit at creating an SQL query to select some records from an Access Database (using Excel VBA).
A cut of one of the tables (let's call it 'table1') has the following columns:
| my_id | your_id | phase |
| 1 | 1 | Open |
| 2 | 1 | Close |
| 3 | 2 | Open |
| 4 | 3 | Close |
| 5 | 2 | Close |
| 6 | 3 | Open |
The field 'my_id' will always be a unique value whereas the 'your_id' field may contain duplicates.
What I would like to do is select everything from the table for the most recent record of the 'your_id' where the phase is 'Close'. So that means in the above example table it would select 5, 4 & 2.
Hope this makes sense, sorry if not - I'm struggling to articulate what I mean!
Thanks
Although from ur example if u just add where conditin as phase='Close' u will get the records of 5,4 and 2. But I am assuming that there might be scenarios (not in ur example) where more than 1 record can come with status as Close for any given your_id so query should look like this
Select * from table1 where my_id in (
Select Max(My_Id) from table1 where phase='Close' group by your_id)

Display another field in the referenced table for multiple columns with performance issues in mind

I have a table of edge like this:
-------------------------------
| id | arg1 | relation | arg2 |
-------------------------------
| 1 | 1 | 3 | 4 |
-------------------------------
| 2 | 2 | 6 | 5 |
-------------------------------
where arg1, relation and arg2 reference to the ids of objects in another object table:
--------------------
| id | object_name |
--------------------
| 1 | book |
--------------------
| 2 | pen |
--------------------
| 3 | on |
--------------------
| 4 | table |
--------------------
| 5 | bag |
--------------------
| 6 | in |
--------------------
What I want to do is that, considering performance issues (a very big table more than 50 million of entries) display the object_name for each edge entry rather than id such as:
---------------------------
| arg1 | relation | arg2 |
---------------------------
| book | on | table |
---------------------------
| pen | in | bag |
---------------------------
What is the best select query to do this? Also, I am open to suggestions for optimizing the query - adding more index on the tables etc...
EDIT: Based on the comments below:
1) #Craig Ringer: PostgreSQL version: 8.4.13 and only index is id for both tables.
2) #andrefsp: edge is almost x2 times bigger than object.
If you can change the structure of the database, you may try to denormalize this part of the database and make table edge with fields id, arg1_name, relation_name, arg2_name. And keep table object without changes to take names for the edge table when you insert or update it.
It is not good. Your data will be duplicates (size of the database will be greater) and it may be difficult to insert or update tables.
But it should be fast to select (no JOINs):
SELECT arg1_name, relation_name, arg2_name
FROM edge;
It won't get cheaper than this:
SELECT o1.object_name, r1.object_name, o2.object_name
FROM edge e
JOIN object o1 ON o1.id = e.arg1
JOIN object r ON r.id = e.relation
JOIN object o2 ON o2.id = e.arg2;
And you don't need more indexes. The one on object.id is the only one needed for this query.
But I seriously doubt that you want to retrieve 50 millions of rows at once, and in no particular order. You still didn't give the full picture.