SQL JOIN based on table contents - sql

I have a single table that contains questions with corresponding references to another table and field that contain the answers. Something like:
I would like to query the questions table and return QID, QuestionText and the value contained in the [ResponseTable].[ResponseField] for each QID. The design seamed flexible at the time. However the app developer is expecting a stored procedure and the SQL developer was counting on an in app solution for this issue.
I am at the end of my rope trying to build this query. How would you suggest accomplishing this task?

I don't think you'll like hearing this answer because it will likely mean some major rework, but I think it's the right answer. Get rid of the questions table and put the questions into new Question fields in the Client1, Client9, and Jobs tables; one for each response.
For example the Client1 table will have these fields:
ColorPref
ColorPrefQuestion
Rating
RatingQuestion
...and so on
Working around that design will be manageable where working around the design you have now will be a headache.

It sounds like a redesign should be considered (storing all responses in one table, for example), but if that's not a possibility then dynamic SQL (using sp_executesql) can be used. However, it can be dangerous to use as it is vulnerable to SQL injection. There are some precautions that can be taken, such as using QUOTENAME on table and column names. This is also a good read before using dynamic SQL: The Curse and Blessings of Dynamic SQL.
DECLARE #tableName NVARCHAR(50)
DECLARE #columnName NVARCHAR(50)
DECLARE #query NVARCHAR(MAX)
SET #tableName = 'Client1'
SET #columnName = 'ColorPref'
SET #query = 'SELECT ' + QUOTENAME(#columnName) + ' FROM ' + QUOTENAME(#tableName)
EXEC sp_executesql #query

Until you get to the rewrite you mentioned, consider the idea of using a view to bring these response tables together.
CREATE VIEW ClientResponses AS
SELECT QID, ResponseField FROM [Client1]
UNION
SELECT QID, ResponseField FROM [Jobs]
UNION
SELECT QID, ResponseField FROM [Client9]
-- ..... add the new tables as they are created
This will
Avoid dynamic SQL
Give you a single place to maintain querying
Provide a pretty simple, readable way to hobble this together

Related

create dynamic temp table in sql

Is there a way to create a dynamic temp table. Below sql code is declaring a variable #tic. I am planning to insert contents from table1 to temp table #df. So instead of giving directly as #df, I am passing as a variable. But below is code is not successful. Can anyone help me here?
declare #tic as varchar(100) = 'df'
select *
into '#' + #tic from (
select * from [dbo].[table1])
select * from #df
Is there a way? Well, I think of the answer as "yes and no and maybe".
As far as I know, there is no way to do this using a local temporary table. As Stu explains in the comment, you would need dynamic SQL to define the table name and then the table would not be visible in the outer scope, because it is a local temporary table.
The "yes" is because one type of temporary table are global temporary tables. These are tables that persist across different scopes. And they are defined using ## instead of # as the prefix. So this works:
declare #tic as varchar(100) = 'df'
declare #sql nvarchar(max);
set #sql = 'select * into ##' + #tic + ' from table1';
select #sql;
exec sp_executesql #sql;
select * from ##df;
(Here is a db<>fiddle.)
The "maybe" is because I'm quite skeptical that you really need this. Dynamic table names are rarely useful in SQL systems, precisely because they depend on dynamic SQL. Introducing dynamic names into SQL (whether columns or tables) is dangerous, both because of the danger of SQL injection and also because it can introduce hard-to-debug syntax errors.
If you are trying to solve a real problem, there might be alternative approaches that are better suited to SQL Server.

Alter table to add dynamic columns based off some previously selected query

Is it possible to alter a table and add columns with a dynamic name/data type based off some previously select query?
The pseudo equivalent for what I'm looking to do in SQL would be:
foreach row in tableA
{
alter tableB add row.name row.datatype
}
This is for SQL Server.
As mentioned, you can do this with dynamic sql. Something along these lines:
Declare #SQL1 nvarchar(4000)
SELECT #SQL1=N'ALTER TABLE mytable'+NCHAR(13)+NCHAR(10)
+N' ADD COLUMN '+ my_new_column_name + ' varchar(25)'+NCHAR(13)+NCHAR(10)
-- SELECT LEN(#SQL1), #SQL1
EXECUTE (#SQL1)
Apart from the fact that this is messy, error prone, a security risk, requires high authorization to execute and needs multiple variables for batches bigger than 4000 characters, it is usually also a bad idea from a design point of view (depending on when/why you are doing this).
Sure, you can do this with dynamic sql.

how to redirect to another database and its table when selecting a specific row from a table in another database

kind people! I have a question about the database.
There are several databases, in each of them there are several tables.
And there is a main database, in which there is only one table containing information
about these databases and tables.
The question is: how to make so that when you select specific information from the table in the main database it automatically forwards you to another database outputting its tables? Sorry for asking this kind of bit tricky question, i have been googling all day and still cannot find solution. i have tried by my own.. but no results.
For now I made something like this, but I am not sure whether I am right
DECLARE #a nvarchar(100)
SET #a= (select * from MasterDB.dbo.Clients_Master_Table);
DECLARE #SQL nvarchar(MAX)
SET #SQL = 'select * from '+ #a +' where Client_Name= somedatabse name'

T SQL Information schema?

We have a lot of data and to save time on making backups I have been tasked with creating a copy of the current database which only contains specified company codes/companies.
I have been told to research into TSQL scripts, information schemas and bulk copies.Im just wondering what the best route is to go down and how to make a start I do have the latest database copy locally.
The script would work by
Allow us at the top to specify name of new database and a list of company codes or ids from company table.
And create a new database and copy over only the data that is relevant
Probably not even close to the best way... but 1 of 100 ways you can do it.
declare #databasename nvarchar(100) = 'roflcopter'
, #destination = 'myschema.mytable'
declare #companylist nvarchar(max) = (select stuff( select ',' +convert(nvarchar(5),companyid) from companytable where database = #databasename),1,1,'')
declare #query nvarchar(max)
= N'use ['+#databasename+']
insert into '+#destination+' /* maybe add dynamic columns here */
select /*same dynamic columns */
from mytable /*or another variable*/
where companyid in('''+#companylist+''')
'
exec (#query)
add your other variables and such to declare destination, future database name (if creating) and anything you want... dynamic sql can be slow, cumbersome and does reconnect. Might want to check the sp_executesql for differences between exec () and executesql
there are a ton of system procedures... ones that find FKs, column names, data types, pks... you can complicate it by building dynamically creating your current schema using (no help from me here) those system procedures, C# to easily 'generate scripts' and execute them.
May also be in the wrong direction you were thinking of.

How to use a view name stored in a field for an sql query?

I have a table with a view_name field (varchar(256)) and I would like to use that field in an sql query.
Example :
TABLE university_members
id | type | view_name | count
1 | professors | view_professors | 0
2 | students | view_students2 | 0
3 | staff | view_staff4 | 0
And I would like to update all rows with some aggregate calculated on the corresponding view (for instance ..SET count = SELECT count(*) FROM view_professors).
This is probably a newbie question, I'm guessing it's either obviously impossible or trivial. Comments on the design, i.e. the way one handle meta-data here (explicity storing DB object names as strings) would be appreciated. Although I have no control over that design (so I'll have to find out the answer anyway), I'm guessing it's not so clean although some external constraints dictated it so I would really appreciate the community's view on this for my personal benefit.
I use SQL Server 2005 but cross-platform answers are welcome.
To do this you would have to do it as a bit of dynamic SQL, something like this might work, obviously you would need to edit to actually match what you are trying to do.
DECLARE #ViewName VARCHAR(500)
SELECT #ViewName = view_name
FROM University_Members
WHERE Id = 1
DECLARE #SQL VARCHAR(MAX)
SET #SQL = '
UPDATE YOURTABLE
SET YOURVALUE = SELECT COUNT(*) FROM ' + #ViewName + '
WHERE yourCriteria = YourValue'
EXEC(#SQL)
The way I see it, you could generate SQL code in a VARCHAR(MAX) variable and then execute it using EXEC keyword. I don't know of any way to do it directly, as you tried.
Example:
DECLARE #SQL VARCHAR(MAX)
SET #SQL = ''
SELECT #SQL = #SQL + 'UPDATE university_members SET count = (SELECT COUNT(*) FROM ' + view_name + ') WHERE id = ' + id + CHAR(10) + CHAR(13) FROM university_members
EXEC #SQL
Warning! This code is not tested. It's just a hint...
Dynamic SQl is the only way to do this which is why this is a bad design choice. Please read the following article if you must be using dynamic SQl in order to protect your data.
http://www.sommarskog.se/dynamic_sql.html
As HLGEM wrote, the fact that you're being forced to use dynamic SQL is a sign that there is a problem with the design itself. I'll also point out that storing an aggregate in a table like that is most likely another bad design choice.
If you need to determine a value at some point, then do that when you need it. Trying to keep a calculated value like that synchronized with your data is almost always fraught with problems - inaccuracy, extra overhead, etc.
There are very rarely situations where storing a value like that is necessary or gives an advantage and those are typically in very large data warehouses or systems with EXTREMELY high throughput. It's nothing that a school or university is likely to encounter.