SELECT data from a table using an intermediate mapping table - sql

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.

Related

Select rows according to another table with a comma-separated list of items

Have a table test.
select b from test
b is a text column and contains Apartment,Residential
The other table is a parcel table with a classification column. I'd like to use test.b to select the right classifications in the parcels table.
select * from classi where classification in(select b from test)
this returns no rows
select * from classi where classification =any(select '{'||b||'}' from test)
same story with this one
I may make a function to loop through the b column but I'm trying to find an easier solution
Test case:
create table classi as
select 'Residential'::text as classification
union
select 'Apartment'::text as classification
union
select 'Commercial'::text as classification;
create table test as
select 'Apartment,Residential'::text as b;
You don't actually need to unnest the array:
SELECT c.*
FROM classi c
JOIN test t ON c.classification = ANY (string_to_array(t.b, ','));
db<>fiddle here
The problem is that = ANY takes a set or an array, and IN takes a set or a list, and your ambiguous attempts resulted in Postgres picking the wrong variant. My formulation makes Postgres expect an array as it should.
For a detailed explanation see:
How to match elements in an array of composite type?
IN vs ANY operator in PostgreSQL
Note that my query also works for multiple rows in table test. Your demo only shows a single row, which is a corner case for a table ...
But also note that multiple rows in test may produce (additional) duplicates. You'd have to fold duplicates or switch to a different query style to get de-duplicate. Like:
SELECT c.*
FROM classi c
WHERE EXISTS (
SELECT FROM test t
WHERE c.classification = ANY (string_to_array(t.b, ','))
);
This prevents duplication from elements within a single test.b, as well as from across multiple test.b. EXISTS returns a single row from classi per definition.
The most efficient query style depends on the complete picture.
You need to first split b into an array and then get the rows. A couple of alternatives:
select * from nj.parcels p where classification = any(select unnest(string_to_array(b, ',')) from test)
select p.* from nj.parcels p
INNER JOIN (select unnest(string_to_array(b, ',')) from test) t(classification) ON t.classification = p.classification;
Essential to both is the unnest surrounding string_to_array.

How to select the nth column, and order columns' selection in BigQuery

I have this huge table upon which I apply a lot of processing (using CTEs), and I want to perform a UNION ALL on 2 particular CTEs.
SELECT *
, 0 AS orders
, 0 AS revenue
, 0 AS units
FROM secondary_prep_cte WHERE purchase_event_flag IS FALSE
UNION ALL
SELECT *
FROM results_orders_and_revenues_cte
I get a "Column 1164 in UNION ALL has incompatible types : STRING,DATE at [97:5]
Obviously I don't know the name of the column, and I'd like to debug this but I feel like I'm going to waste a lot of time if I can't pin-point which column is 1164.
I also think this is a problem of the order of columns between the CTEs, so I have 2 questions:
How do I identify the 1164th column
How do I order my columns before performing the UNION ALL
I found this similar question but it is for MSSQL, I am using BigQuery
You can get information from INFORMATION_SCHEMA.COLUMNS but you'll need to create a table or view from the CTE:
CREATE OR REPLACE VIEW `project.dataset.secondary_prep_view` as select * from (select 1 as id, "a" as name, "b" as value)
Then:
SELECT * FROM dataset.INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'secondary_prep_view';

I need to create a VIEW from an existing TABLE and MAP an additional COLUMN to that VIEW

I am fairly new to SQL. What I am trying to do is create a view from an existing table. I also need to add a new column to the view which maps to the values of an existing column in the table.
So within the view, if the value in a field for Col_1 = A, then the value in the corresponding row for New_Col = C etc
Does this even make sense? Would I use the CASE clause? Is mapping in this way even possible?
Thanks
The best way to do this is to create a mapping or lookup table
For example consider the following LOOKUP table.
COL_A NEW_VALUE
---- -----
A C
B D
Then you can have a query like this:
SELECT A.*, LOOK.NEW_VALUE
FROM TABLEA AS A
JOIN LOOKUP AS LOOK ON A.COL_A = LOOK.COL_A
This is what DimaSUN is doing in his query too -- but in his case he is creating the table dynamically in the body of the query.
Also note, I'm using a JOIN (which is an inner join) so only results in the lookup table will be returned. This could filter the results. A LEFT JOIN there would return all data from A but some of the new columns might be null.
Generally, a view is an instance of a table/a replica provided that there is no alteration to the original table. So, as per your query you can manipulate the data and columns in a view by using case.
Create View viewname as
Select *,
case when column=a.value then 'C'
....
ELSE
END
FROM ( Select * from table) a
If You have restricted list of replaced values You may hardcode that list in query
select T.*,map.New_Col
from ExistingTable T
left join (
values
('A','C')
,('B','D')
) map (Col_1,New_Col) on map.Col_1 = T.Col_1
In this sample You hardcode 'A' -> 'C' and 'B' -> 'D'
In general case You better may to use additional table ( see Hogan answer )

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;

Ensuring two columns only contain valid results from same subquery

I have the following table:
id symbol_01 symbol_02
1 abc xyz
2 kjh okd
3 que qid
I need a query that ensures symbol_01 and symbol_02 are both contained in a list of valid symbols. In other words I would needs something like this:
select *
from mytable
where symbol_01 in (
select valid_symbols
from somewhere)
and symbol_02 in (
select valid_symbols
from somewhere)
The above example would work correctly, but the subquery used to determine the list of valid symbols is identical both times and is quite large. It would be very innefficient to run it twice like in the example.
Is there a way to do this without duplicating two identical sub queries?
Another approach:
select *
from mytable t1
where 2 = (select count(distinct symbol)
from valid_symbols vs
where vs.symbol in (t1.symbol_01, t1.symbol_02));
This assumes that the valid symbols are stored in a table valid_symbols that has a column named symbol. The query would also benefit from an index on valid_symbols.symbol
You could try use a CTE like;
WITH ValidSymbols AS (
SELECT DISTINCT valid_symbol
FROM somewhere
)
SELECT mt.*
FROM MyTable mt
INNER JOIN ValidSymbols v1
ON mt.symbol_01 = v1.valid_symbol
INNER JOIN ValidSymbols v2
ON mt.symbol_02 = v2.valid_symbol
From a performance perspective, your query is the right way to do this. I would write it as:
select *
from mytable t
where exists (select 1
from valid_symbols vs
where t.symbol_01 = vs.valid_symbol
) and
exists (select 1
from valid_symbols vs
where t.symbol_02 = vs.valid_symbol
) ;
The important component is that you need an index on valid_symbols(valid_symbol). With this index, the lookup should be pretty fast. Appropriate indexes can even work if valid_symbols is a view, although the effect depends on the complexity of the view.
You seem to have a situation where you have two foreign key relationships. If you explicitly declare these relationships, then the database will enforce that the columns in your table match the valid symbols.