TSQL Cross Apply determining which databases to query - sql

I'm writing (in .NET) a login screen that allows the login to connect to a SQL Server. Once they've put in the server name and their credentials, it should then show a list of databases for them to connect to, but, the databases need to be of the right structure. This will be identified within each database by the existence of a ref.Config table, and a row in that table with appropriate values. There may be a whole bunch of other databases on the server for other purposes. I don't know at designtime.
Ideally what I'd like to do is something like this:
SELECT m.name
FROM MASTER.sys.databases m
CROSS APPLY (SELECT *
FROM {m.name}.INFORMATION_SCHEMA.TABLES t
WHERE t.TABLE_SCHEMA = 'ref'
AND t.TABLE_NAME 'Config') dbs
CROSS APPLY (SELECT *
FROM {m.name}.ref.Config c
WHERE c.KeyName = 'DatabaseMagicNumber'
AND c.KeyValue = '12345678') config
WHERE HAS_DBACCESS(m.name) = 1
ORDER BY m.name
Where m.name gets substituted into the subqueries after evaluation (I know the above isn't valid SQL). Is there a way to do this, or do I have to run a query on each database? I am unable to have a stored procedure on the server at this point. Ideally I just want one SQL statement that will return the names of all databases that conform to the structure I expect.

Related

Access 2010 Query Syntax error

(1) The Access Database is being used as way to connect to 2 Teradata databases
(2) Currently it has 6 linked tables to 2 Teradata databases
(3) It doesn't matter to me if it is an Access Query or a pass-through query to Teradata
(4) The goal is to put something in an Excel macro that will query the 2 Teradata bases and return some information that will be included in a macro
I'm trying to create a SQL query in Access with SQL that works in Teradata. When I try to run the query, I get am error message (Syntax error in FROM clause) and the 1st Join is highlighted. I've written queries in Access before but nothing this complex. We are using Access 2010. Any suggestions on the syntax error would be greatly appreciated. Thanks for the help.......
select veh_mgmt_csr.cst_bo_item.veh_lgcy_nbr as unit, veh_mgmt_csr.cst_bo_item.veh_odmtr_qty as mileage, veh_mgmt_csr.rm_cust_mast.upp_lgl_cust_nam as cust_legal_name, veh_mgmt_csr_rv.cst_address.addr_st_nam as address,
veh_mgmt_csr_rv.cst_address.addr_cty_nam as city, veh_mgmt_csr_rv.cst_address.addr_postl_cde as zip, veh_mgmt_csr_rv.cst_address.stprov_cde as state, veh_mgmt_csr_rv.cst_address.cntry_iso_cde as country, veh_mgmt_csr_rv.cst_address.addr_phn_nbr as location_phone, e.contact_name, e.contact_phone
from veh_mgmt_csr.cst_buyer_order
Join veh_mgmt_csr.cst_bo_item on veh_mgmt_csr.cst_buyer_order.bo_id = veh_mgmt_csr.cst_bo_item.bo_id and veh_mgmt_csr.cst_bo_item.veh_invy_stat_dsc = 'SOLD'
Join veh_mgmt_csr.rm_cust_mast on veh_mgmt_csr.cst_buyer_order.rm_cust_id = veh_mgmt_csr.rm_cust_mast.rm_cust_id
Join VEH_MGMT_CSR_RV.cst_address on veh_mgmt_csr.rm_cust_mast.prim_addr_id = veh_mgmt_csr_rv.cst_address.addr_id and veh_mgmt_csr_rv.cst_address.record_status = 'A'
left join (select veh_mgmt_csr.cst_buyer_order.e_o_id cust_nbr,trim(veh_mgmt_csr.cst_individual.indiv_upp_frst_nam) || ' ' || trim(veh_mgmt_csr.cst_individual.indiv_upp_last_nam) contact_name,
VEH_MGMT_CSR_RV.CST_PHONE_NBR.phn_nbr contact_phone from veh_mgmt_csr.cst_e_o_cntct
left join veh_mgmt_csr.cst_individual on veh_mgmt_csr.cst_e_o_cntct.indiv_id = veh_mgmt_csr.cst_individual.indiv_id
left join VEH_MGMT_CSR_RV.CST_PHONE_NBR on veh_mgmt_csr.cst_e_o_cntct.indiv_id = VEH_MGMT_CSR_RV.CST_PHONE_NBR.indiv_id and VEH_MGMT_CSR_RV.CST_PHONE_NBR.prim_phn_ind = 1
qualify rank() over (partition by veh_mgmt_csr.cst_e_o_cntct.e_o_id order by contact_name asc) = 1) e on veh_mgmt_csr.rm_cust_mast.rm_cust_id = e.cust_nbr
where veh_mgmt_csr.cst_individual.veh_lgcy_nbr = '8B5RG1'
and veh_mgmt_csr.cst_e_o_cntct.cntry_iso_cde in ('US','CA')
and extract(year from veh_mgmt_csr.cst_e_o_cntct.bo_dte) = 2017
As a beginner to SQL, please note that while most RDBMS's including MS Access and Teradata may run ANSI-SQL (basic, standard DDL/DML statements), almost no two RDBMS's share the same dialects (ANSI-plus). Each maintains their own styles and specific methods.
Additionally, do note MS Access comes in two folds: 1) a GUI .exe application and 2) a database engine (ACE/JET database engine). Over time it has been conflated to be the same but they are not. See this meta post. The former .exe application by default connects to the default database engine but this default can be switched out for other backends like Teradata.
However, the method of connection between frontend GUI and backend database (i.e., linked tables, pass-through queries, application code) will differ in the SQL dialect used.
linked tables => MS Access SQL dialect
pass-through queries => Backend RDBMS database dialect
application code (i.e., VBA) => Backend RDBMS database dialect
You may be attempting to run Teradata SQL on MS Access linked tables, hence violating #1 with resulting syntax errors. As seen below with proper indentation, there are several incompatible syntaxes in your attempted query:
MS Access uses only one period qualifier between table name and column name. Likely you may be referencing a named schema, only available in Teradata.
MS Access does not use JOIN by itself but requires INNER, LEFT, or RIGHT (no OUTER);
MS Access requires parentheses whenever pairs of tables are used in JOIN clauses;
MS Access does not support window functions such as RANK() OVER...;
MS Access uses & in concatenation not double pipes || and uses Year() instead of extract(year ...); and does not use qualify, possibly strictly a Teradata method;
MS Access requires AS for column aliases such as for contact_name and contact_phone.
SQL
SELECT veh_mgmt_csr.cst_bo_item.veh_lgcy_nbr AS unit,
veh_mgmt_csr.cst_bo_item.veh_odmtr_qty AS mileage,
veh_mgmt_csr.rm_cust_mast.upp_lgl_cust_nam AS cust_legal_name,
veh_mgmt_csr_rv.cst_address.addr_st_nam AS address,
veh_mgmt_csr_rv.cst_address.addr_cty_nam AS city,
veh_mgmt_csr_rv.cst_address.addr_postl_cde AS zip,
veh_mgmt_csr_rv.cst_address.stprov_cde AS state,
veh_mgmt_csr_rv.cst_address.cntry_iso_cde AS country,
veh_mgmt_csr_rv.cst_address.addr_phn_nbr AS location_phone,
e.contact_name,
e.contact_phone
FROM veh_mgmt_csr.cst_buyer_order
JOIN veh_mgmt_csr.cst_bo_item
ON veh_mgmt_csr.cst_buyer_order.bo_id = veh_mgmt_csr.cst_bo_item.bo_id
AND veh_mgmt_csr.cst_bo_item.veh_invy_stat_dsc = 'SOLD'
JOIN veh_mgmt_csr.rm_cust_mast
ON veh_mgmt_csr.cst_buyer_order.rm_cust_id = veh_mgmt_csr.rm_cust_mast.rm_cust_id
JOIN veh_mgmt_csr_rv.cst_address
ON veh_mgmt_csr.rm_cust_mast.prim_addr_id = veh_mgmt_csr_rv.cst_address.addr_id
AND veh_mgmt_csr_rv.cst_address.record_status = 'A'
LEFT JOIN
(
SELECT veh_mgmt_csr.cst_buyer_order.e_o_id cust_nbr,
Trim(veh_mgmt_csr.cst_individual.indiv_upp_frst_nam)
|| ' ' ||
Trim(veh_mgmt_csr.cst_individual.indiv_upp_last_nam) contact_name,
veh_mgmt_csr_rv.cst_phone_nbr.phn_nbr contact_phone
FROM veh_mgmt_csr.cst_e_o_cntct
LEFT JOIN veh_mgmt_csr.cst_individual
ON veh_mgmt_csr.cst_e_o_cntct.indiv_id = veh_mgmt_csr.cst_individual.indiv_id
LEFT JOIN veh_mgmt_csr_rv.cst_phone_nbr
ON veh_mgmt_csr.cst_e_o_cntct.indiv_id = veh_mgmt_csr_rv.cst_phone_nbr.indiv_id
AND veh_mgmt_csr_rv.cst_phone_nbr.prim_phn_ind = 1
qualify rank() OVER (partition BY veh_mgmt_csr.cst_e_o_cntct.e_o_id
ORDER BY contact_name ASC) = 1) e
ON veh_mgmt_csr.rm_cust_mast.rm_cust_id = e.cust_nbr
WHERE veh_mgmt_csr.cst_individual.veh_lgcy_nbr = '8B5RG1'
AND veh_mgmt_csr.cst_e_o_cntct.cntry_iso_cde IN ('US','CA')
AND extract(year FROM veh_mgmt_csr.cst_e_o_cntct.bo_dte) = 2017;
Per your update do the following:
To keep above Teradata syntax, create 2 pass-through queries (using same ODBC connection as linked tables) for each database.
Run same make-table query (SELECT * INTO myAccessTable FROM myTeradataPassThroughQuery) to move Teradata results into Access local tables.
Join the two new tables for your final end result using Access SQL syntax.

declare and use a string variable in oracle sql developer 4.1.3.20

So at work we've been using Oracle SQL Developer 4.1.3.20 lately to query our data so I wrote this snippet of code (its actually much longer by 8 or so more table joins):
select
s.NAME as "Shipment ID"
,k.STATUS_ID as "Status"
,u.SCR_NO as "SPID"
from xyz.DRUG_KIT k
left join xyz.DR_SHIP s on s.ID = k.SHIPMENT_ID
left join xyz.USR_PAT u on u.PAT_ID = k.PAT_ID
When I have to switch to another table in a different database, I have been copy pasting everywhere above where you see 'xyz' but surely there has to ba better way? I use to use SQL Server Management Studio 2014 but we had to switch to this once we began using oracle databases. How would I declare a varchar(?) variable in Oracle SQL Developer so I can just use that one variable in place of XYZ and at the top of the query I just can set that variable equal whatever necessary in one place if that makes sense. It becomes cumbersome when I copy paste something wrong on one line when I"m trying to join about 10+ tables together to retrieve data.
XYZ is the owner of the table, not a different database. If you are connected as user XYZ, then you don't need to prefix XYZ to the table name, it's only required if you are attempting to access objects owned by a different "SCHEMA".
You can change the current "parsing" schema of your session by issuing an alter session command:
ALTER SESSION SET CURRENT_SCHEMA=PDQ;
and then select from objects the user PDQ has granted you select privs on:
select
s.NAME as "Shipment ID"
,k.STATUS_ID as "Status"
,u.SCR_NO as "SPID"
from DRUG_KIT k
left join DR_SHIP s on s.ID = k.SHIPMENT_ID
left join USR_PAT u on u.PAT_ID = k.PAT_ID;
You can then alter your schema back to XYZ and perform the same select statement and get different results based on the contents of the tables as owned by XYZ:
ALTER SESSION SET CURRENT_SCHEMA=XYZ;
select
s.NAME as "Shipment ID"
,k.STATUS_ID as "Status"
,u.SCR_NO as "SPID"
from DRUG_KIT k
left join DR_SHIP s on s.ID = k.SHIPMENT_ID
left join USR_PAT u on u.PAT_ID = k.PAT_ID;
To make the DR_SHIP table owned by PDQ accessable from the XYZ schema, a private synonym to PDQ.DR_SIP can be created in place of the table onwed by XYZ:
CREATE OR REPLACE SYNONYM DR_SHIP FOR PDQ.DR_SHIP;
On the other hand if you want the PDQ version of the DRUG_KIT table accessible from all SCHEMAs in the DB, you can create a public synonym:
CREATE OR REPLACE PUBLIC SYNONYM DRUG_KIT FOR PDQ.DRUG_KIT;
If none of the above meet your needs, you can use SQL*Plus style substitution variables:
ACCEPT user;
select
s.NAME as "Shipment ID"
,k.STATUS_ID as "Status"
,u.SCR_NO as "SPID"
from &user..DRUG_KIT k
left join &user..DR_SHIP s on s.ID = k.SHIPMENT_ID
left join &user..USR_PAT u on u.PAT_ID = k.PAT_ID;
But you need to understand that these won't necessarily work outside of SQL*Plus, SQL Developer, SQLcl, and possibly a couple of other SQL development environments.
At the database level, you probably want public synonyms. https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_7001.htm

How can I set a limitation for one column on all SQL selects?

We have a database that holds data for numerous customers. We want to give customers access to the database, but only to the data that belongs to them. Parsing the select to then insert in the where clause "and Company.Name = 'Acme'" strikes me as weak because SQL selects can be very complex and handling 100% of all cases may be difficult.
Is there some way to do the equivalent of (I know this is not valid SQL):
select * from * where Company.Name = 'Acme' and (passed_in_select)
You can nest a full select in as an inner part of a large select. Is there some way to do the above? This way it's a very simple restriction on the select and that is likely to work 100% of the time.
Here is a system solution called "virtual private database" for Oracle database:
https://docs.oracle.com/cd/B28359_01/network.111/b28531/vpd.htm
For other databases look whether there is similar built-in solution.
But there is very simple solution using the WITH clause:
WITH
tab_a__ AS (SELECT * FROM tab_a WHERE comp="xy"),
tab_b__ AS (SELECT * FROM tab_b WHERE comp="xy")
SELECT ... //original select
You just have to find all used tables in the select, add __ behind and add the CTEs to the WITH clause.
Notes: Some databases do not support WITH clause though it is an SQL standard. Some databases can have alias length limitation you could exceed by adding the suffix.
select * from
(
select * from table_a
) outer_table_a
where outer_table_a.col_a = 'test'
I do this sort of thing often especially when I want to perform some aggregation on the data in the inner query (sum, max, etc.) I do this with SQL Server, I do not know if it is valid with other DBMS but I would be surprised if it were not.
I don't know if I would rely on this approach to effectively grant permissions. Perhaps views would allow you lock things down a bit tighter. It sounds like you're planning to tack something on dynamically to a query that you may not have written? In that case whomever writes that query could transform your column of interest which would result in visibility over things you didn't intend, like:
select * from
(
select 'test' as col_a, launch_codes from table_a
) outer_table_a
where outer_table_a.col_a = 'test'

SQL Server 2012 Performance issue using FULLTEXT

I'm using SQL Server 2012 Standard and I have some issue using the CONTAINS clause on a query.
My query:
select *
from
calles as c
INNER JOIN
colonias as col ON c.ID_Colonia = col.ID_Colonia
where
CONTAINS(c.Nombre,#Busqueda) OR CONTAINS(col.Nombre,#Busqueda)
If I use only one contains the time of the search is about 200 ms but if I use both it is about 10s (that's a lot of time). I try a workaround to do it using UNION like this:
select *
from
calles as c
INNER JOIN
colonias as col ON c.ID_Colonia = col.ID_Colonia
where
CONTAINS(c.Nombre,#Busqueda)
UNION
select *
from
calles as c
INNER JOIN
colonias as col ON c.ID_Colonia = col.ID_Colonia
where
CONTAINS(col.Nombre,#Busqueda)
And the query time is about 200ms again. But I think that the second code is clumsy. Do I have some error?
FULLTEXT index in SQL Server is a service which is (kinda) external to the RDBMS engine.
It accepts the search string and returns a list of key values from the table (which need then to be joined with the table itself to be sure they're still there).
So in fact you are joining two more tables in your query and apply an OR condition to the result of the join.
SQL Server's optimizer is not especially smart when it comes to constructs like this.
Replacing an OR condition with a UNION is a legitimate and commonly used optimization technique.

Cross-database queries with numbered database name

I'm a bit of a novice when it comes to SQL Server 2005. I have a database containing most of the stored procedures and tables (we'll call it 'GrandDatabase'). Each user has its own separate database named after the user's numbered ID. So I have a database list as follows, for example:
GrandDatabase
100
101
102
...
I need to join tables across the GrandDatabase and a user's database. I've read elsewhere that the following should work, when executed from GrandDatabase:
SELECT
*
FROM
GrandDatabase.User INNER JOIN
100.dbo.UserInfo ON GrandDatabase.User.UserID = 100.dbo.UserInfo.UserID
This gives me a syntax error, complaining about the '.' right after the first reference to the 100 database. I did a little tweaking and discovered that this code works fine when I use non-numbered databases (for instance, replacing the '100' above with 'User100'). Does anybody know how to make this work with numbered database names?
Thanks!
Chris
Try using [100].dbo.UserInfo instead of just the 100.
Try putting the numbers into square brackets and using aliases, e.g.:
SELECT *
FROM GrandDatabase.User
INNER JOIN [100].dbo.UserInfo u
ON GrandDatabase.User.UserID = u.UserID
Try enclosing the database name with brackets:
SELECT
*
FROM
GrandDatabase.User INNER JOIN
[100].dbo.UserInfo ON GrandDatabase.User.UserID = [100].dbo.UserInfo.UserID