union all on views based on table - sql

i have a database table with a list of sql views
i want to create a new view or stored procedure that based on which views are in that table will return those views unioned
like this
SELECT ALINE1, HOME, EMAIL, EXPIRE, EDATE7, type
FROM dbo.campaign_membership_30
UNION ALL
SELECT ALINE1, HOME, EMAIL, EXPIRE, EDATE7, type
FROM dbo.campaign_membership_30n
UNION ALL
SELECT ALINE1, HOME, EMAIL, EXPIRE, EDATE7, type
FROM dbo.campaign_membership_60n
UNION ALL
SELECT ALINE1, HOME, EMAIL, EXPIRE, EDATE7, type
FROM dbo.campaign_membership_today
UNION ALL
SELECT ALINE1, BOOKNO, EMAIL, DEPART, DEP7, type
FROM dbo.depart_151days
UNION ALL
SELECT ALINE1, BOOKNO, EMAIL, DEPART, DEP7, type
FROM dbo.depart_90Days

I think you're asking for a way to have a table drive which views are part of the query. In other words, if your table had the following rows:
dbo.View1
dbo.View2
dbo.View3
Your query would return the union of of those three views; correct?
A caveat: I highly question the underlying relational structures that require you to do this. A better data model would probably render this unnecessary. But I'm assuming you don't have the ability to change model, so try this:
Assume the three views listed above are in a field called "ViewName" on a table called "ViewsToReturn." Construct a query that first unions all of the views, then filters it according to the names of the views in ViewsToReturn. Like this:
SELECT * FROM
(SELECT Field1, Field2, 'dbo.View1' AS ViewName FROM dbo.View1
UNION
SELECT Field1, Field2, 'dbo.View2' AS ViewName FROM dbo.View2
UNION
SELECT Field1, Field2, 'dbo.View3' AS ViewName FROM dbo.View3
UNION
SELECT Field1, Field2, 'dbo.View4' AS ViewName FROM dbo.View4) AllRecords
WHERE AllRecords.TableName IN
SELECT ViewName FROM ViewsToReturn
This solution is neither elegant nor performant, but should do the trick if I understand your question correctly.
Good luck!

There are a lot of major caveats here.
First, using dynamic SQL is often dangerous. It can open up your database to SQL injection attacks. If you don't understand what these are then you need to do a lot of educating of yourself before I would suggest that you use dynamic SQL.
Second, this kind of a pattern (storing tables and columns in a table from which to generate SQL) is a really bad pattern. There are many problems with it. I can't suggest alternative solutions though without knowing more about your application/problem space.
That said, the following is a simplistic version of how it might work in SQL Server 2005. This does not include error handling, etc.
DECLARE #sql VARCHAR(MAX) -- NVARCHAR if you use unicode characters in table names
SELECT
#sql = dbo.Concatenated_Rows('SELECT col1, col2 FROM ' + MT.table_name + ' UNION ALL ')
FROM
dbo.My_Table MT
-- Remove the extra UNION ALL
SELECT #sql = SUBSTRING(#sql, 1, LEN(#sql) - 11)
EXEC(#sql)
The function Concatenated_Rows would have to be written. You could alternatively use a cursor or maybe even FOR XML to create the concatenated string. Here's a link that does a good job explaining many of the possible methods and does a good job of comparing them.
Again, there are probably better solutions out there then going this route, especially if this is more than a one-off task.

Related

Is there a way I create a SQL view of multiple tables based on a query of the table schema?

I have a SQL database where there are multiple tables maintained by other people which I would like to join up to create a view. The trouble is the number of tables keeps expanding! The columns and character lengths are the same.
I can get as far as creating a list of the tables by using
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE 'Table%'.
At the moment I have a union all query like the below
SELECT * FROM table1
UNION ALL
SELECT * FROM table2;
but the table list keeps growing. Is there any I can create something to loop thought the tables? Something like
*SELECT * FROM (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE 'Table%') UNION ALL*
I know that wouldn't work but I'm hoping there's some sort of trick to get to go all the way! This is on SQL Server 2012 if that helps.
Thanks
declare #s nvarchar(max);
set #s=''
select
#s=#s +'(select * from [' + s.name + '])'+
case when ROW_NUMBER() over (order by s.name) != count(*) over ()
then
' UNION ALL '
else
''
end
from
sys.tables as s
where s.name like 't%'
print #s;
There is a bit more to think about, you need to make sure the field counts are the same before hand also to be on the safe side, may be best to avoid select * and use field names that you require. This should produce the SQL statement to run in a stored proc as detailed in the comments. Spend some time error trapping this and doing neccessary checks for continuity in the field names as mentioned.

Creating a list in SQL and iterating through the list after the FROM line

I want to access some table like Toyota_Corolla, Toyota_Camry, Toyota_Prius, Toyota_Rav4
Instead of typing out multiple SELECT statements like the following:
SELECT * FROM Toyota_Corolla;
SELECT * FROM Toyota_Camry;
SELECT * FROM Toyota_Prius;
SELECT * FROM Toyota_Rav4;
Is there a way to create a list of strings like ['Corolla', 'Camry', 'Prius', Rav4'] and iterate through the list after the FROM line to something similar to:
SELECT * FROM 'Toyota_'` + 'some loop to iterate the list of car model'
I know for my example, it's easier to just type out the whole thing, but what about the situation when Toyota has hundred of models?
This is MS SQL Server DBMS
No. First, you should fix your data model so you have a single table with an additional column for the Toyota model. That is the right way to store the data.
With the data you have, you can emulate this with a view:
create view vw_toyota as
select 'Corolla' as toyota_model, t.* from Toyota_Corolla t union all
select 'Camry' as toyota_model, t.* from Toyota_Camry t union all
select 'Prius' as toyota_model, t.* from Toyota_Prius t union all
select 'Rav4' as toyota_model, t.* from Toyota_Rav4 t;
This also adds the source table information.
And then do:
select *
from vw_toyota;

Use sql to select data, then insert row in first position of result set only (not update database)

If I select a column in a table, say 10 rows, can I use SQL to insert a 'please select' row into the first position using SQL only? I want to insert it in the result set only - not the database.
First you should know this is a bad idea.
You are confusing your presentation layer and your database layer. Forcing SQL to do things like output status messages or feedback to users is an antipattern to be avoided.
That being said, if the column is of a string type (char, varchar, etc), you can do something like:
SELECT 'Please Select'
UNION ALL
SELECT TOP 10 Varcharfield
FROM Mytable
If it's numeric then no unless you cast it to a string type.
Here's something you can try:
SELECT u.YourVarcharColumnName
FROM (
SELECT 1 AS rID, 'Please select' AS YourVarcharColumnName
UNION
SELECT 2 AS rID, YourVarcharColumnName
FROM YourTableName
) u
ORDER BY u.rID;
I placed rID in place and sorted by it as an extreme-case-measure when your intended first rowset does not come out on top...
However, you should keep in mind that YourVarcharColumnName should be (as it says) a string. You'll have to convert it to a string if it's a non-string column.
As #JNK mentioned it.. I thought I should edit my post as well:
Please first try:
SELECT 'Please select' AS YourVarcharColumnName
UNION
SELECT YourVarcharColumnName
FROM YourTableName
Which is similar to what the others have posted. If you ever experience what I've been unfortunate to encounter and 'Please select' doesn't come out on top, please refer to the query I posted at the top.. Thanks!
SELECT '(Please select)'
UNION
SELECT ColumnName FROM TableName

SQL Server query brings unmatched data with BETWEEN filter

I'm querying on my products table for all products with code between a range of codes, and the result brings a row that should't be there.
This is my SQL query:
select prdcod
from products
where prdcod between 'F-DH1' and 'F-FMS'
order by prdcod
and the results of this query are:
F-DH1
F-DH2
F-DH3
FET-RAZ <-- What is this value doing here!?
F-FMC
F-FML
F-FMS
How can this odd value make it's way into the query results?
PS: I get the same results if I use <= and >= instead of between.
According to OP request promoted next comment to answer:
Seems like your collation excludes '-' sign - this way results make sense, FE is between FD and FM.
:)
between and >= and <= are primarily used for numeric operations (including dates). You're trying to use this for strings, which are difficult at best to determine how those operators will interpret the each string.
Now, while I think I understand your goal here, I'm not entirely sure it's possible using SQL Server queries. This may be some business logic (thanks to the product codes) that needs implemented in code. Something like the Entity Framework or Linq-to-SQL may be better suited to get you the data you're looking for.
How about adding AND LEFT(prdcod, 2) = 'F-'?
Try replacing the "-" with a space so the order is what you would expect:
DECLARE #list table(word varchar(50))
--create list
INSERT INTO #list
SELECT 'F-DH1'
UNION ALL
SELECT 'F-DH2'
UNION ALL
SELECT 'F-DH3'
UNION ALL
SELECT 'FET-RAZ'
UNION ALL
SELECT 'F-FMC'
UNION ALL
SELECT 'F-FML'
UNION ALL
SELECT 'F-FMS'
--original order
SELECT * FROM #list order by word
--show how order changes
SELECT *,replace(word,'-',' ') FROM #list order by replace(word,'-',' ')
--show between condition
SELECT * FROM #list where replace(word,'-',' ') between 'F DH1' and 'F FMS'

how do I query multiple SQL tables for a specific key-value pair?

Situation: A PHP application with multiple installable modules creates a new table in database for each, in the style of mod_A, mod_B, mod_C etc. Each has the column section_id.
Now, I am looking for all entries for a specific section_id, and I'm hoping there's another way besides "Select * from mod_a, mod_b, mod_c ... mod_xyzzy where section_id=value"... or even worse, using a separate query for each module.
If the tables are changing over time, you can inline code gen your solution in an SP (pseudo code - you'll have to fill in):
SET #sql = ''
DECLARE CURSOR FOR
SELECT t.[name] AS TABLE_NAME
FROM sys.tables t
WHERE t.[name] LIKE 'SOME_PATTERN_TO_IDENTIFY_THE_TABLES'
-- or this
DECLARE CURSOR FOR
SELECT t.[name] AS TABLE_NAME
FROM TABLE_OF_TABLES_TO_SEACRH t
START LOOP
IF #sql <> '' SET #sql = #sql + 'UNION ALL '
SET #sql = 'SELECT * FROM [' + #TABLE_NAME + '] WHERE section_id=value '
END LOOP
EXEC(#sql)
I've used this technique occasionally, when there just isn't any obvious way to make it future-proof without dynamic SQL.
Note: In your loop, you can use the COALESCE/NULL propagation trick and leave the string as NULL before the loop, but it's not as clear if you are unfamiliar with the idiom:
SET #sql = COALESCE(#sql + ' UNION ALL ', '')
+ 'SELECT * FROM [' + #TABLE_NAME + '] WHERE section_id=value '
What about?
SELECT * FROM mod_a WHERE section_id=value
UNION ALL
SELECT * FROM mod_b WHERE section_id=value
UNION ALL
SELECT * FROM mod_c WHERE section_id=value
I have two suggestions.
Perhaps you need to consolidate all your tables. If they all contain the same structure, then why not have one "master" module table, that just adds one new column identifying the module ("A", "B", "C", ....)
If your module tables are mostly the same, but you have a few columns that are different, you might still be able to consolidate all the common information into one table, and keep smaller module-specific tables with those differences. Then you would just need to do a join on them.
This suggestion assumes that your query on the column section_id you mention is super-critical to look up quickly. With one query you get all the common information, and with a second you would get any specific information if you needed it. (And you might not -- for instance if you were trying to validate the existense of the section, then finding it in the common table would be enough)
Alternatively you can add another table that maps section_id's to the modules that they are in.
section_id | module
-----------+-------
1 | A
2 | B
3 | A
... | ...
This does mean though that you have to run two queries, one against this mapping table, and another against the module table to pull out any useful data.
You can extend this table with other columns and indices on those columns if you need to look up other columns that are common to all modules.
This method has the definite disadvanage that the data is duplicated.
Perhaps some additional info would help, but it sounds like you have the solution already. You will have to select from all the tables with a section_id. You could use joins instead of a table list, joining on section_id. For example
select a.some_field, b.some_field....
from mod_a a
inner join mod_b b on a.section_id = b.section_id
...
where a.section_id = <parameter>
You could also package this up as a view.
Also notice the field list instead of *, which I would recommend if you were intending to actually use *.
Well, there are only so many ways to aggregate information from multiple tables. You can join, like you mentioned in your example, or you can run multiple queries and union them together as in borjab's answer. I don't know if some idea of creating a table that intersects all the module tables would be useful to you, but if section_id was on a table like that you'd be able to get everything from a single query. Otherwise, I applaud your laziness, but am afraid to say, I don't see any way to make that job eaiser :)
I was going to suggest the same think as borjab. The only problem with that is that you will have to update all of these queries if you add another table. The only other option I see is a stored procedure.
I did think of another option here, or at least an easier way to present this. You can also use a view to these multiple tables to make them appear as one, and then your query would look cleaner, be easier to understand and you wouldn't have to rewrite a long union query when you wanted to do other queries on these multiple tables.
SELECT * FROM (
SELECT * FROM table1
UNION ALL
SELECT * FROM table2
UNION ALL
SELECT * FROM table3
) subQry
WHERE field=value
An option from the database side would be to create a view of the UNION ALL of the various tables. When you add a table, you would need to add it to the view, but otherwise it would look like a single table.
CREATE VIEW modules AS (
SELECT * FROM mod_A
UNION ALL
SELECT * FROM mod_B
UNION ALL
SELECT * FROM mod_C
);
select * from modules where section_id=value;