Let two table act as one - sql

Is there any way to let two tables act as one? I have two identical tables, the only difference is that one contains recent data and the other one older data. Is there any way to do something like this?
select *
from customers a,
(rentals union archrentals) r
where a.serno = r.custserno
and r.rentaldate > YYYYMMDD;

Why don't you create a view like this?
CREATE VIEW v_allRentals
AS
SELECT * form rentals
UNION ALL
SELECT * FROM archrentals
In this way you can use v_allRentals without worrying every time you create a query that you are forgetting the old ones.
Thanks,
Mucio

Use a temporary table
select *
INTO #allcustomers
from customers a,
(rentals union archrentals) r
where a.serno = r.custserno
and r.rentaldate > YYYYMMDD;
then you can use the temptable to query the results.

Related

SELECT data from a table using an intermediate mapping table

I have one mapping table, let us say VW_MAPPING.
VW_MAPPING has columns and data like this:
MetricTable
A VW_A
B VW_B
C VW_C
D VW_D
E VW_E
Now I want to perform a conditional SELECT.
Like
when Metric = A then SELECT * FROM VW_A;
when Metric = B then SELECT * FROM VW_B;
i) All the tables have the same number of columns.
ii)The underlying tables don't have the metric column
Basically I will get the Metric value from an Input Form. So I want to show the data accordingly. If A is given then I will show data from VW_A. And for which response we have to use which table that we will get from VW_MAPPING.
What I want is something like this :
SELECT * FROM variable_table_name;
variable_table_name = SELECT TABLE FROM VW_MAPPING WHERE METRIC = form input
This is the pseudo code
I cannot use PL/SQL.
Although probably not the most efficient way, I would be inclined to do:
with v as (
select 'A' as metric, a.* from vw_a a union all
select 'B', b.* from vw_b b union all
select 'C', c.* from vw_c c union all
. . .
)
select v.*
from v
where metric = :metric;
That is, the mapping table is not actually useful. Instead of a mapping table, you should have one table with all the metrics and an additional column that identifies the metric. You can also do this using a view instead of a table.
Is this what are you expecting?
SELECT * FROM VW_A where Metric in
(selct Metric from VW_MAPPING);
SELECT * FROM VW_B where Metric in
(selct Metric from VW_MAPPING);
There are a few ways of doing this.
By far the best is to handle this in the user interface layer - the code behind your input form should decide which query to execute. This has a number of benefits: the code is much simpler, it will probably be faster, and your database does the "storing and modifying data" task, not the "react to user choices" task.
If you really can't do that, you can use a UNION
select *
from VW_A
where metric = 'A'
union
select *
from VW_A
where metric = 'B'
...
This embeds the decision in your query, rather than looking at your mapping table.
If you really want to use your mapping table, you'll need to use dynamic SQL, but you say that you can't use PL/SQL.
I'm not aware of any other options.

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;

Is there something I can change to make my view run faster?

I just created a view but it is really slow, since my actual table has something around 800k rows.
Is there something I can change in the actual sql code to make it run faster?
Here is how it looks now:
Select B.*
FROM
(Select A.*, (select count(B.KEY_ID)/77
FROM book_new B
where B.KEY_ID = A.KEY_ID) as COUNT_KEY
FROM
(select *
from book_new
where region = 'US'
and (actual_release_date is null or
actual_release_date >= To_Date( '01/07/16','dd/mm/yy'))
) A
) B
WHERE B.COUNT_KEY = 1
OR (B.COUNT_KEY > 1 AND B.NEW_OLD <> 'Old')
The most obvious things to do are add indexes:
Add an index on book_new(key_id)
Add an index on book_new(region, actual_release_date)
These are probably sufficient. It is possible that rewriting the query would help, but this is a good beginning. If you want to rewrite the query, it would help if you described the logic you are trying to implement.
There are many ways to solve this issue based on your needs
You can create an indexed view
You can create an index in the base tables which are used in this view.
You can use the required columns in the SELECT statement instead of using SELECT * FROM,
If the table contains many columns but you require only few columns, you can create a NON CLUSTERED INDEX with INCLUDE COLUMNS option which will reduce the LOGICAL READS.
For starters, replace the scalar subquery for COUNT_KEY with a windowed COUNT(*).
SELECT * FROM
(
select book_new.*, COUNT(*) OVER ( PARTITION BY book_new.key_id)/77 COUNT_KEY
from book_new
where region = 'US'
and (actual_release_date is null or
actual_release_date >= To_Date( '01/07/16','dd/mm/yy'))
)
WHERE count_key = 1 OR ( count_key > 1 AND new_old <> 'Old' )
This way, you only go through the BOOK_NEW table one time.
BTW, I agree with other comments that this query makes little sense.

SQL Server : compare two tables with UNION and Select * plus additional label column

I've been playing around with the sample on Jeff' Server blog to compare two tables to find the differences.
In my case the tables are a backup and the current data. I can get what I want with this SQL statement (simplified by removing most of the columns). I can then see the rows from each table that don't have an exact match and I can see from which table they come.
SELECT
MIN(TableName) as TableName
,[strCustomer]
,[strAddress1]
,[strCity]
,[strPostalCode]
FROM
(SELECT
'Old' as TableName
,[JAS001].[dbo].[AR_CustomerAddresses].[strCustomer]
,[JAS001].[dbo].[AR_CustomerAddresses].[strAddress1]
,[JAS001].[dbo].[AR_CustomerAddresses].[strCity]
,[JAS001].[dbo].[AR_CustomerAddresses].[strPostalCode]
FROM
[JAS001].[dbo].[AR_CustomerAddresses]
UNION ALL
SELECT
'New' as TableName
,[JAS001new].[dbo].[AR_CustomerAddresses].[strCustomer]
,[JAS001new].[dbo].[AR_CustomerAddresses].[strAddress1]
,[JAS001new].[dbo].[AR_CustomerAddresses].[strCity]
,[JAS001new].[dbo].[AR_CustomerAddresses].[strPostalCode]
FROM
[JAS001new].[dbo].[AR_CustomerAddresses]) tmp
GROUP BY
[strCustomer]
,[strAddress1]
,[strCity]
,[strPostalCode]
HAVING
COUNT(*) = 1
This Stack Overflow Answer gives me a much cleaner SQL query but does not tell me from which table the rows come.
SELECT * FROM [JAS001new].[dbo].[AR_CustomerAddresses]
UNION
SELECT * FROM [JAS001].[dbo].[AR_CustomerAddresses]
EXCEPT
SELECT * FROM [JAS001new].[dbo].[AR_CustomerAddresses]
INTERSECT
SELECT * FROM [JAS001].[dbo].[AR_CustomerAddresses]
I could use the first version but I have many tables that I need to compare and I think that there has to be an easy way to add the source table column to the second query. I've tried several things and googled to no avail. I suspect that maybe I'm just not searching for the correct thing since I'm sure it's been answered before.
Maybe I'm going down the wrong trail and there is a better way to compare the databases?
Could you use the following setup to accomplish your goal?
SELECT 'New not in Old' Descriptor, *
FROM
(
SELECT * FROM [JAS001new].[dbo].[AR_CustomerAddresses]
EXCEPT
SELECT * FROM [JAS001].[dbo].[AR_CustomerAddresses]
) a
UNION
SELECT 'Old not in New' Descriptor, *
FROM
(
SELECT * FROM [JAS001].[dbo].[AR_CustomerAddresses]
EXCEPT
SELECT * FROM [JAS001new].[dbo].[AR_CustomerAddresses]
) b
You can't add the table name there because union, except, and intersection all compare all columns. This means you can't differentiate between them by adding the table name to the query. A group by gives you control over what columns are considered in finding duplicates so you can exclude the table name.
To help you with the large number of tables you need to compare you could write a sql query off the metadata tables that hold table names and columns and generate the sql commands dynamically off those values.
Derive one column using table names like below
SELECT MIN(TableName) as TableName
,[strCustomer]
,[strAddress1]
,[strCity]
,[strPostalCode]
,table_name_came
FROM
(SELECT 'Old' as TableName
,[JAS001].[dbo].[AR_CustomerAddresses].[strCustomer]
,[JAS001].[dbo].[AR_CustomerAddresses].[strAddress1]
,[JAS001].[dbo].[AR_CustomerAddresses].[strCity]
,[JAS001].[dbo].[AR_CustomerAddresses].[strPostalCode]
,'[JAS001].[dbo].[AR_CustomerAddresses]' as table_name_came
FROM [JAS001].[dbo].[AR_CustomerAddresses]
UNION ALL
SELECT 'New' as TableName
,[JAS001new].[dbo].[AR_CustomerAddresses].[strCustomer]
,[JAS001new].[dbo].[AR_CustomerAddresses].[strAddress1]
,[JAS001new].[dbo].[AR_CustomerAddresses].[strCity]
,[JAS001new].[dbo].[AR_CustomerAddresses].[strPostalCode]
,'[JAS001new].[dbo].[AR_CustomerAddresses]' as table_name_came
FROM [JAS001new].[dbo].[AR_CustomerAddresses]
) tmp
GROUP BY [strCustomer]
,[strAddress1]
,[strCity]
,[strPostalCode]
,table_name_came
HAVING COUNT(*) = 1

SQL Contains query

I have two tables (A and B) that contain ID's however in table B some records have these ID's grouped together e.g the IDExec column may consist of a record that looks like 'id1 id2'. I'm trying to find the ID's in table A that do not appear in table B. I thought that by using something like:
SELECT *
FROM A
WHERE NOT EXISTS( SELECT *
FROM B
WHERE Contains(A.ExecID, B.ExecID))
This isn't working as contains needs the 2nd parameter to be string, text_lex or variable.
Do you guys have a solution to this problem?
To shed more light on the above problem the table strucutres are as follows:
Table A (IDExec, ProdName, BuySell, Quantity, Price, DateTime)
Table B (IDExec, ClientAccountNo, Quantity)
The C# code I've created to manipulate the buysell data in Table A groups up all the buysell's of the same product on a given day. The question now is how would you guy normalise this so I'm not bastardizing IDExec? Would it be better to create a new ID column in Table B called AllocID and link the two tables like that? So something like this:
Table A (IDExec, AllocID, ProdName, BuySell, Quantity, Price, DateTime)
Table B (AllocID, ClientAccountNo, Quantity)
This data should be normalized, storing multiple values in one field is a bad idea.
A workaround is using LIKE:
SELECT *
FROM A
WHERE NOT EXISTS( SELECT *
FROM B
WHERE ' '+B.ExecID+' ' LIKE '% '+A.ExecID+' %')
This is using space delimited values per your example.
This is kind of crude, but it will give you all of the entries in A that are not contained in B.
SELECT * FROM A WHERE A.ExecID not in (SELECT ExecID from B);
I have a very simple solution. It's called normalization. Proper modeling can work wonders for query simplicity and accuracy.
However, you may be stuck with what you have. Assuming ExecID is a string in both tables, try this:
select *
from A
where not exists(
select *
from B
where ExecID like '%' || a.ExecID || '%';
This is a horrible query as it performs a complete table scan of B for every row in A and the subquery is susceptible to false hits, so maybe you can do better, but your best course ultimately is a touch of database refactoring.