Complex join with coalesce - sql

I have a table that I am stuffing demographic info into and then using dynamic sql to build a table from that. The demographic info comes from surveys and some of the surveys have checkboxes. With checkboxes people can select multiple values.
So I need to enter all the choices as a comma seperated list.
CREATE TABLE Demographics
(
QID NVARCHAR(15)
,userid NVARCHAR(50)
,question NVARCHAR(800)
,choice NVARCHAR(1000)
)
...
...
--Demographics(QID,userid,question,choice)
--'Insert checkbox (type 5)
INSERT INTO Demographics
SELECT CAST(q.QID AS NVARCHAR(15))
,ri.userid
,q.QuestionText
,ac.Choice
FROM ResponseInfo ri --response details
JOIN Responses r ON ri.ResponseID = r.ResponseID --actual response
JOIN Questions q ON r.QID = q.QID --question info
JOIN AnswerChoices ac ON r.QID = ac.QID --answer choice text
WHERE (q.QuestionTypeID = 5 AND q.QID = ac.QID
AND r.IsOther = 0
AND q.QID = 16
)
AND ri.userid IN (SELECT userid FROM #Users) AND r.Response = ac.Sequence
ORDER BY ri.userid
...
dynamic sql stuff
...
EXEC sp_exesql #sql
My results look like this:
users | question_15 | choice_15 | question_16 | choice_16 |
bill | age? | 37 | favorite color? | red |
bill | age? | 37 | favorite color? | green |
But it needs to be:
users | question_15 | choice_15 | question_16 | choice_16 |
bill | age? | 37 | favorite color? | red,green |
I tried doing
,COALESCE(ac.Choice + ',','') + ac.Choice
It gave me
bill | age? | 37 | favorite color? | red,red |
Can anyone help me sort this out?

It looks like you are using SQL Server. If you're using a recentish version of SQL Server (e.g., SQL Server 2005 or later), you'd likely be better off storing your data as XML.
That lets you use XPATH/XQUERY in SQL to manipulate the XML data to get what you want. I think you'll find that easier to do.
http://www.simple-talk.com/sql/learn-sql-server/sql-server-xml-cribsheet/
http://www.simple-talk.com/sql/t-sql-programming/beginning-sql-server-2005-xml-programming/

Found this.
Very helpful. My code could probably use some cleaning up.

Related

How to fetch non-translated data in this given scenario

Suppose in my application, I am inserting a few data(here questions) in a specific table in SQL SERVER.
-------------------------------------------
TBL_QUESTIONS
-------------------------------------------
question_id|subj_code_id|question_text
-----------|------------|------------------
1 | 1 | A basic question
-----------|------------|------------------
2 | 1 | Another Question...
-----------|------------|-------------------
3 | 1 | Again a question
Now anything that I entered in this table has a culture of en-US.
Currently, I have the following cultures.
-------------------------------
TBL_LANG_CULTURE
-------------------------------
lang_id|lang_name|culture_name
-------|---------|-------------
1 | English | en-US
2 | Hindi | hi-IN
3 | French | fr-FR
Now there is another table which is storing translated part of those questions...
----------------------------------------
TBL_TRANSLATED_QUESTINS
---------------------------------------
question_id| lang_culture|question_text
-----------|-------------|---------------
1 | hi-In | एक बुनियादी सवाल
-----------|-------------|----------------
2 | hi-In | एक और सवाल
-----------|-------------|----------------
2 | fr-FR | Une autre question
Now, If I want to display how many questions are there which is not translated yet in French. Then the output should look like this,
question_id| English_TEXT | TRANSLATED_TEXT
-----------|-------------------|-----------------
1 | A basic question |
-----------|-------------------|-----------------
3 | Again a question |
And now, If I want to display how many questions are there which is not translated yet in Hindi. Then the output should look like this,
question_id| English_TEXT | TRANSLATED_TEXT
-----------|-------------------|-----------------
3 | Again a question |
Try this-
SELECT *
FROM TBL_QUESTIONS A
LEFT JOIN TBL_TRANSLATED_QUESTINS B
ON A.question_id = B.question_id
AND lang_culture = 'fr-FR' -- To check French
WHERE B.question_id IS NULL
This result can be achieved by using join and then filter. Something like below might help you -
SELECT TQ.question_id, TQ.question_text, TTQ.question_text
FROM TBL_QUESTIONS TQ
LEFT JOIN (SELECT question_id, lang_culture, question_text
FROM TBL_TRANSLATED_QUESTINS TT2
WHERE TTQ.question_id IN (SELECT DISTINCT question_id
FROM TBL_LANG_CULTURE TLC
INNER JOIN TBL_TRANSLATED_QUESTINS TTQ ON TLC.culture_name = TTQ.lang_culture
WHERE lang_name = 'French')) ON TQ.question_id = TTQ.question_id
WHERE TQ.question_id IS NULL
I have not tried it yet, but this should work for you.

How to get column names from a query?

I have a specific query with joins and aliases, and I need to retrieve columns name for a REST request in Talend.
I'm using Talend Open Studio for Data Integration 6.2 and I've got an Oracle 11g database with a read-only account. I can execute scripts with Talend, For example the query:
select
u.name as "user",
f.name as "food",
e.rate
from
Users as u
join Eval as e on u.user_id = e.user_id
join Food as f on e.food_id = f.food_id
where
1 = 1
should give the following result:
+------+--------+------+
| user | food | rate |
+------+--------+------+
| Baba | Donuts | 16.0 |
| Baba | Cheese | 20.0 |
| Keke | Pasta | 12.5 |
| Keke | Cheese | 15.0 |
+------+--------+------+
And I try to get the columns (in the right order) as follows by using scripts or Talend:
+--------+
| Column |
+--------+
| user |
| food |
| rate |
+--------+
Is there a way to query the Oracle database to get the columns or using talend to retrieve them?
UPDATE
Thanks to Marmite Bomber, a duplicate has been identified here for the Oracle approach. Now we need a Talend approach to the problem.
You can try this on a tJavaRow, following your DBInput component :
for (java.lang.reflect.Field field: row1.getClass().getDeclaredFields()) {
context.columnName = field.getName();
System.out.println("Field name is " + context.columnName );
}
Spotted on talend help center here : https://community.talend.com/t5/Design-and-Development/resolved-how-to-get-the-column-names-in-a-data-flow/td-p/99172
You can extend this, and put the column list on your outputflow :
//add this inside the loop, and 'columnNames' as an output row in tJavaRow schema
output_row.columnNames+=context.columnName+";";
With a tNormalize after tJavaRow, you shoud get the expected result.
Here´s a link to an oracle community thread which should answer your question
community.oracle.com
I am not able to write a comment, so posting this as an answer:
SELECT column_name
FROM all_tab_cols
WHERE table_name = 'table_name_here'

SQL Server : query with subquery involving select from previous selection

I'm trying to create a query in SQL Server to determine how many times a person's name shows up in a list, but also that list will be unknown, so I would have to get the actual name from the previous select index... It's hard to explain so I'll show the query first and hopefully someone can help.
SELECT
SpeakerName, Spoken,
(SELECT COUNT(SpeakerName)
FROM tbl_SpeakerCard_Log
WHERE SpeakerName = 'SpeakerName[i]' AND SpeakDate = '3-9-16') as TimesSpoken
FROM
tbl_SpeakerCard_Log
WHERE
AID = ####
ORDER BY
GeneralComment ASC
So basically, in SpeakerName[i], I'd like to somehow get the SpeakerName from the outer Select. The output should come out something like this
+-------------+--------+-------------+
| SpeakerName | Spoken | TimesSpoken |
+-------------+--------+-------------+
| Holly | 0 | 4 |
| Robert | 1 | 5 |
| Mike | 1 | 2 |
+-------------+--------+-------------+
Try this:
select x.SpeakerName, x.Spoken, COUNT(*) as TimesSpoken
from tbl_SpeakerCard_Log x
WHERE AID = ####
and x.SpeakDate = '3-9-16'
group by x.SpeakerName, x.Spoken
Don't have SSMS installed on this computer so can't test it.

SQL JOIN on Dynamic Column based on Variable

I have an image summary table [summary] that will serve as a reporting table in the near future. There is a reference table [views] and a third table that the image team populates [TeamImage]. The summary table has 1 row per part number (table has distinct part numbers) and many columns of image views (TOP, BOT, FRO, BAC, etc.). The [views] table lists each of these views with an id field, which is an IDENTITY field. The [TeamImage] table contains part numbers and views (part number field is not unique as the part numbers will be listed multiple times as they have image views).
Example:
TABLE [summary]
Part_Number | TOP | BOT | FRO | BAC |
12345 | | | | |
67890 | | | | |
TABLE [views]
id | View |
1 | TOP |
2 | BOT |
3 | FRO |
4 | BAC |
TABLE [TeamImage]
PartNum | View |
12345 | TOP |
12345 | BOT |
12345 | FRO |
12345 | BAC |
67890 | FRO |
67890 | BAC |
Here's what I need in the end:
TABLE [summary]
Part_Number | TOP | BOT | FRO | BAC |
12345 | 1 | 1 | 1 | 1 |
67890 | | | 1 | 1 |
I could run several update queries but I have 27 views and about 2 million part numbers. I was hoping I could run something like below, even though I know I cannot use a variable as the column name:
DECLARE #id int = (SELECT max(id) FROM [views]), #ViewType nvarchar(3);
WHILE #id IS NOT NULL
BEGIN
SELECT #ViewType = (SELECT [View] FROM [views] WHERE id = #id);
UPDATE a
SET a.[#ViewType] = '1'
FROM [summary] a
INNER JOIN [TeamImage] b
AND a.[Part_Number] = b.[PartNum]
WHERE b.[View] = #ViewType;
SELECT #id = max(id) FROM [views] WHERE id < #id;
END;
Basically, I was hoping to use a variable to grab the different views from the [views] table (id = 27 down to id=1...could have counted up but doesn't matter) and populate the corresponding field in the [summary] table.
I know the SET a.[#ViewType] = '1' won't work, and a colleague of mine mentioned using VB but didn't know if that really was the most efficient option. I understand that I could use a PIVOT on the [TeamImage] table, but I'm not sure that will allow me to update my [summary] table (which has many more fields in it than just the image views). It still seems I need something that will effectively loop through update queries. I could write 4 update queries, one for each view (although my real table has 27 views), but I need something more dynamic in case we add views in the future.
To create your final summary, you can do via a simple pivot, yet this is fixed to the few codes you've done... but I know SQL does have a PIVOT command, but not directly familiar enough with it.
select
TA.PartNum,
max( case when TA.TeamImage = 'TOP' then '1' else ' ' end ) as TOPview,
max( case when TA.TeamImage = 'BOT' then '1' else ' ' end ) as BOTview,
max( case when TA.TeamImage = 'FRO' then '1' else ' ' end ) as FROview,
max( case when TA.TeamImage = 'BAC' then '1' else ' ' end ) as BACview
from
TeamImage TA
group by
TA.PartNum
Obviously simple to expand, but you can also look into the "PIVOT" syntax
I asked the question a little better here: SQL output as variable in VB.net and was able to receive an answer that worked for what I was looking for. I appreciate DRapp providing a solution through PIVOT, but I think the VB way will be easier for me moving forward. In short, using VB with ExecuteScalar and ExecuteNonQuery, I was able to re-write my query using the variables I had above.

Assistance with SQL multi-table query - returning duplicate results

We use an online project management system, and I'm trying to extend it somewhat.
It has the following tables of interest:
todo_itemStatus:
+--------------+-----------------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-----------------------+------+-----+---------------------+----------------+
| itemStatusId | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| itemId | int(10) unsigned | NO | MUL | 0 | |
| statusDate | datetime | NO | | 0000-00-00 00:00:00 | |
| statusKey | tinyint(3) unsigned | NO | | 0 | |
| memberId | mediumint(8) unsigned | NO | | 0 | |
+--------------+-----------------------+------+-----+---------------------+----------------+
This table keeps track of when a task is complete, and also keeps the status of all task changes.
There's then a project table, and an 'item' (or task) table.
I basically want to be able to extract a list of projects, with details on the percentage of tasks complete. However, for now I'd be happy if I could just list each task in a project with details on whether they're complete.
As far as I'm aware, the best way to get the most recent status of a task is to choose an todo_itemStatus where the statusDate is the newest, or the itemStatusId is the largest whilst itemId equals the task I'm interested.
I tried a query like this:
<pre>
select todo_item.itemId, todo_item.title, todo_itemStatus.statusKey, todo_itemStatus.statusDate
from todo_item, todo_project, todo_itemStatus
where todo_item.projectId = todo_project.projectId
and todo_project.projectId = 13
and todo_itemStatus.itemId = todo_item.itemId
and todo_itemStatus.statusDate = (
select MAX(todo_itemStatus.statusDate)
from todo_itemStatus key1 where todo_itemStatus.itemId = key1.itemId);
</pre>
However, this yields all status updates with output like this:
+--------+-----------------------------------------------------------------------------+-----------+---------------------+
| itemId | title | statusKey | statusDate |
+--------+-----------------------------------------------------------------------------+-----------+---------------------+
| 579 | test complete item - delete me | 1 | 2009-07-28 13:04:38 |
| 579 | test complete item - delete me | 0 | 2009-07-28 14:12:12 |
+--------+-----------------------------------------------------------------------------+-----------+---------------------+
Which isn't what I want, as I only want one task entry returning with the statusKey / statusDate from the most recent entry in the todo_itemStatus table.
I know I've been a bit vague in my description, but I didn't want to write a massively long message. I can provide much more detail if necessary.
Please can someone suggest what I'm doing wrong? It's been a long time since I've done any real database stuff, so I'm a bit unsure what I'm doing wrong here...
Many thanks!
Dave
You should look into using the DISTINCT keyword (Microsoft SQL Server)
EDIT: I've just re-read your question and I think that the GROUP BY clause is more suited in this situation. You should read http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/, however essentially what you need to do is first select the columns that you are interested in using a GROUP BY clause:
SELECT todo_itemStatus.itemStatusId, MAX(todo_itemStatus.statusDate)
FROM todo_item, todo_project, todo_itemStatus
WHERE todo_item.projectId = todo_project.projectId
AND todo_itemStatus.itemId = todo_item.itemId
AND todo_project.projectId = 13
GROUP BY itemStatusId
We then self-join to this set of id's to get the rest of the columns we are interested in:
SELECT
todo_item.itemId,
todo_item.title,
todo_itemStatus.statusKey,
todo_itemStatus.statusDate
FROM todo_item
JOIN todo_itemStatus
ON todo_itemStatus.itemId = todo_item.itemId
JOIN
(SELECT todo_itemStatus.itemStatusId, MAX(todo_itemStatus.statusDate)
FROM todo_item, todo_project, todo_itemStatus
WHERE todo_item.projectId = todo_project.projectId
AND todo_itemStatus.itemId = todo_item.itemId
AND todo_project.projectId = 13
GROUP BY itemStatusId) AS x
ON todo_itemStatus.itemStatusId = x.itemStatusId
I've experimented some more and the following query does what I want:
select todo_item.itemId, todo_item.title, todo_itemStatus.statusKey, todo_itemStatus.statusDate from todo_itemStatus, todo_item where todo_item.itemId = todo_itemStatus.itemId and todo_item.projectId = 13 and todo_itemStatus.statusDate = (select MAX(status.statusDate) from todo_itemStatus as status where status.itemId = todo_item.itemId);
So I'm now happy. Thanks for all the help and the suggestions.
Dave.