selecting specific fields during runtime from table - sql

How do we select fields dynamically?
I've got a table Table1:
+----------+-------+----+
| Table1Id | x | y |
+==========+=======+====+
| 52 | alex | aa |
+----------+-------+----+
| 43 | liza | aa |
+----------+-------+----+
| 21 | harry | bb |
+----------+-------+----+
| 21 | harry | bb |
+----------+-------+----+
I'd like to join on this Table2:
+----------+----------+--------+------+
| Table2Id | Table1Id | aa | bb |
+==========+==========+========+======+
| 1 | 52 | red | tall |
+----------+----------+--------+------+
| 2 | 43 | blue | thin |
+----------+----------+--------+------+
| 3 | 21 | orange | fat |
+----------+----------+--------+------+
The result I'm looking for is:
+-------+-------+----+----------+
| xyzid | x | y | NewField |
+=======+=======+====+==========+
| 52 | alex | aa | red |
+-------+-------+----+----------+
| 43 | liza | aa | blue |
+-------+-------+----+----------+
| 21 | harry | bb | fat |
+-------+-------+----+----------+
As you can see, Table1 has data in the y column the exact field name to grab from Table2.
How do select specific fields from a table, where those fields are actually stored as data in another table?

Just another option which would be a bit more dynamic
No need for the CASE.
table2 could have any number of columns.
No need to convert columns to string or a common datatype.
BUT ... I suspect a bit less performant than The Impalar's answer.
I should note that this is for 2017+. Oddly enough, 2016 requires a "string literal" for JSON_VALUE or JSON_QUERY.
Example or dbFiddle
Select Distinct
xyzid = A.Table1Id
,A.x
,A.y
,NewField = JSON_VALUE(B.JS,'$.'+A.Y)
From Table1 A
Join ( Select [Table1Id]
,JS=(Select B1.* For JSON Path,Without_Array_Wrapper)
From Table2 B1 ) B
on A.[Table1Id]=B.[Table1Id]
Results
UPDATE - 2016 Version
Select xyzid = A.Table1Id
,A.x
,A.y
,NewField = (select max([Value]) from OpenJSON(B.JS) where [key]=A.Y collate database_default)
From Table1 A
Join ( Select [Table1Id]
,JS=(Select B1.* For JSON Path,Without_Array_Wrapper )
From Table2 B1 ) B
on A.[Table1Id]=B.[Table1Id]
2nd 2016 Approach -- Perhaps slightly more performant dbFiddle
Select xyzid = A.Table1Id
,A.x
,A.y
,NewField = B.Value
From Table1 A
Join (
Select [Table1Id]
,[Key]
,Value
From Table2 B1
Cross Apply OpenJson((Select B1.* For JSON Path,Without_Array_Wrapper ) )
) B
on A.[Table1Id]=B.[Table1Id]
and A.Y = B.[Key] collate database_default

You can select columns dynamically using CASE, as in:
select
a.Table1Id as xyzid,
a.x,
a.y,
case
when a.y = 'aa' then b.aa
when a.y = 'bb' then b.bb
end as NewField
from (select distinct * from table1) a
join table2 b on b.Table1Id = a.Table1Id
Result:
xyzid x y newfield
------ ------ --- --------
52 alex aa red
43 liza aa blue
21 harry bb fat
See running example at DB Fiddle.

Related

How to concatenate results of multiple rows

I currently have a view in SQL Server, something like this:
Table1:
Id
Desc
Mex
Table2:
Id
IdTab1
Desc
The view select everything from Table1 left joined on Table2 on Id - IdTab1
Now I have a table 3 joined with Table2 that has like these fields:
Table3:
Id
IdTab2
Code (VarChar(3))
I would like to have in the select of the view a new field Code that contains every code in table 3 concatenated with the char ' ' without changing the record displayed from the old query (so like doing a group by concat) every Code that matches the join.
I saw some other posts but neither of them used this kind of approach.
For example using this:
declare #result varchar(500)
set #result = ''
select #result = #result + ModuleValue + ', '
from TableX where ModuleId = #ModuleId
But I have faced two problems.
I could not use declare in the view (probably because of wrong syntax), and also I have to do this group by and I can't figure out how.
Example result basic view
ID | IDTAB2 | DESC1 | DESC2 | MEX
1 | 2 | aa | bb | 4
2 | 1 | ab | cc | 2
2 | 2 | bb | bc | 2
Example result joined Table3
ID | IDTAB2 | DESC1 | DESC2 | MEX | CODE
1 | 2 | aa | bb | 4 | CS
1 | 2 | aa | bb | 4 | NN
2 | 1 | ab | cc | 2 | AF
2 | 2 | bb | bc | 2 | DC
2 | 2 | bb | bc | 2 | KK
2 | 2 | bb | bc | 2 | JD
Example result needed
ID | IDTAB2 | DESC1 | DESC2 | MEX | CODENEW
1 | 2 | aa | bb | 4 | CS NN
2 | 1 | ab | cc | 2 | AF
2 | 2 | bb | bc | 2 | DC KK JD
Considering your output from "Example result joined Table3", you can try this below option based on your SQL server version-
For MSSQL-2016 and earlier- Demo
SELECT DISTINCT A.ID,A.IDTAB2,A.DESC1,A.DESC2,A.MEX,
SUBSTRING(
(
SELECT ' '+ B.CODE AS [text()]
FROM your_table B
WHERE B.ID = A.ID
AND B.IDTAB2 = A.IDTAB2
AND B.DESC1 = A.DESC1
AND B.DESC2 = A.DESC2
AND B.MEX = A.MEX
ORDER BY B.ID,B.IDTAB2,B.DESC1,B.DESC2,B.MEX
FOR XML PATH ('')
), 2, 1000) [C_Name]
FROM your_table A
For MSSQL-2017 or newer- Demo
SELECT ID,IDTAB2,DESC1,DESC2,MEX,
STRING_AGG ( CODE, ' ' )
FROM your_table
GROUP BY ID,IDTAB2,DESC1,DESC2,MEX

Is it possible for foxpro in sql statement to fill the winners_name column base on condition maximum score and id with different names

Is it possible for foxpro in sql statement to add and fill a winners_name column base on condition maximum score and id with different names.
I have created a sql statement but it was not supported by foxpro, is there other alternative to do this rather than using a loop (I like sql statement for faster result even in 50k row lines)
SELECT * , ;
(SELECT TOP 1 doc_name FROM Table1 as b1 WHERE ALLTRIM(b1.id) = a.id ORDER BY b1.score DESC, b1.id) as WINNERS_NAME ;
FROM Table1 as a
I have only 1 table, with columns [ name, id, score ]
A sample table would be like this
NAME | ID | SCORE |
BEN | 101 | 5 |
KEN | 101 | 2 |
ZEN | 101 | 3 |
JEN | 103 | 4 |
REN | 103 | 3 |
LEN | 102 | 5 |
PEN | 102 | 4 |
ZEN | 102 | 3 |
The result would be like this (winners_name is tag on ID)
NAME | ID | SCORE | WINNERS_NAME
BEN | 101 | 5 | BEN
KEN | 101 | 2 | BEN
ZEN | 101 | 3 | BEN
JEN | 103 | 4 | PEN
REN | 103 | 3 | PEN
LEN | 102 | 5 | LEN
PEN | 103 | 5 | PEN
ZEN | 102 | 3 | LEN
Try this approach:
SELECT
a.NAME,
a.ID,
a.SCORE,
b.WINNERS_NAME
FROM Table1 a
INNER JOIN
(
SELECT t1.ID, t1.NAME AS WINNERS_NAME
FROM
(
SELECT ID, SCORE, MIN(NAME) AS NAME
FROM Table1
GROUP BY ID, SCORE
) t1
INNER JOIN
(
SELECT ID, MAX(SCORE) AS MAX_SCORE
FROM Table1
GROUP BY ID
) t2
ON t1.ID = t2.ID AND
t1.SCORE = t2.MAX_SCORE
) b
ON a.ID = b.ID
ORDER BY
a.ID;
Follow the link below for a demo running in MySQL (though the syntax should still work on FoxPro):
Demo

SQL query join- unrelated tables

Can someone help me to join the two tables without any primary or secondary keys. Sample table is
TABLE 1
| ID | NAME |
| 1 | x |
| 2 | Y |
| 3 | z |
TABLE 2
| Num | NAME | DATE |
| 52 | X | 12-aug-17 |
| 53 | X | 11-apr-17 |
| 62 | X | 10-aug-11 |
| 12 | y | 2-jan-16 |
| 23 | Y | 3-apr-18 |
I want retrieve data from X
select *
from table2
where name = 'x';
| Num | NAME | DATE |
| 52 | X | 12-aug-17 |
| 53 | X | 11-apr-17 |
| 62 | X | 10-aug-11 |
Now I will get three data from table2. I'm little stuck after this step. I want to get top of data the from table 2 and combine with table one.
I want final output should be
| ID | NAME | Num | DATE |
| 1 | x | 52 | 12-aug-17 |
Can someone suggest me how can I join this table? Its easy to join when we have any primary key but here not the case
Thanks
You can use this:
SELECT TOP(1) table1.ID, table2.Num, table2.Name, table2.DATE
FROM table2 INNER JOIN table1 ON table1.NAME = table2.NAME
WHERE table2.NAME = 'x'
ORDER BY table2.DATE ASC
OR
SELECT table1.ID, table2.Num, table2.Name, table2.DATE
FROM table1 INNER JOIN
(SELECT TOP(1) * FROM table2 WHERE NAME = 'x' ORDER BY DATE ASC) table2
ON table1.NAME = table2.NAME
You need to get the maximum DATE using a subquery, as in:
select t1.id, t2.*
from table1 t1
join table2 t2 on t2.name = t1.name
where t2.date = (
select max(date) from table2 where name = 'x'
);

I need a specific output

I have to get a specific output format from my tables.
Let's say I have a simple table with 2 columns name and value.
table T1
+---------------+------------------+
| Name | Value |
+---------------+------------------+
| stuff1 | 1 |
| stuff1 | 1 |
| stuff2 | 2 |
| stuff3 | 1 |
| stuff2 | 4 |
| stuff2 | 2 |
| stuff3 | 4 |
+---------------+------------------+
I know the values are in the interval 1-4. I group it by name and value and count number of the same rows as Number and get the following table:
table T2
+---------------+------------------+--------+
| Name | Value | Number |
+---------------+------------------+--------+
| stuff1 | 1 | 2 |
| stuff2 | 2 | 2 |
| stuff3 | 1 | 1 |
| stuff3 | 4 | 1 |
+---------------+------------------+--------+
Here is the part when I need your help! What should I do if I want to get these format?
table T3
+---------------+------------------+--------+
| Name | Value | Number |
+---------------+------------------+--------+
| stuff1 | 1 | 2 |
| stuff1 | 2 | 0 |
| stuff1 | 3 | 0 |
| stuff1 | 4 | 0 |
| stuff2 | 1 | 0 |
| stuff2 | 2 | 2 |
| stuff2 | 3 | 0 |
| stuff2 | 4 | 0 |
| stuff3 | 1 | 1 |
| stuff3 | 2 | 0 |
| stuff3 | 3 | 0 |
| stuff3 | 4 | 1 |
+---------------+------------------+--------+
Thanks for any suggestions!
You start with a cross join to generate all possible combinations and then left-join in the results from your existing query:
select n.name, v.value, coalesce(nv.cnt, 0) as "Number"
from (select distinct name from table t) n cross join
(select distinct value from table t) v left outer join
(select name, value, count(*) as cnt
from table t
group by name, value
) nv
on nv.name = n.name and nv.value = v.value;
Variation on the theme.
Differences between Gordon Linoff and Owen existing answers.
I prefer GROUP BY to get the Names rather than a DISTINCT. This may have better performance in a case like this. (See Rob Farley's still relevant article.)
I explode the subqueries into a series of CTEs for clarity.
I use table T2 as the question now labels the group results set instead of showing that as as subquery.
WITH PossibleValue AS (
SELECT 1 Value
UNION ALL
SELECT Value + 1
FROM PossibleValue
WHERE Value < 4
),
Name AS (
SELECT Name
FROM T1
GROUP BY Name
),
NameValue AS (
SELECT Name
,Value
FROM Name
CROSS JOIN
PossibleValue
)
SELECT nv.Name
,nv.Value
,ISNULL(T2.Number,0) Number
FROM NameValue nv
LEFT JOIN
T2 ON nv.Name = T2.Name
AND nv.Value = T2.Value
Yet another solution, this time using a Table Value Constructor in a CTE to build a table of name value combinations.
WITH value AS
( SELECT DISTINCT t.name, v.value
FROM T1 AS t
CROSS JOIN (VALUES (1),(2),(3),(4)) AS v (value)
)
SELECT v.name AS 'Name', v.value AS 'Value', COUNT(t.name) AS 'Number'
FROM value AS v
LEFT JOIN T1 AS t ON t.value = v.value AND t.name = v.name
GROUP BY v.name, v.value, t.name;

How to do a SQL self join with nulls

I am using MS SQL 2008. My table looks like this:
| Name | Code | Amt |
| ----- | ---- | ---- |
| April | A | 1.23 |
| Barry | A | 2.34 |
| Barry | B | 3.45 |
| Cliff | A | 4.56 |
| Cliff | B | 5.67 |
| Cliff | C | 6.78 |
I need the output to be this:
| Name | Code_A | Code_B | Code_C |
| ----- | ------ | ------ | ------ |
| April | 1.23 | NULL | NULL |
| Barry | 2.34 | 3.45 | NULL |
| Cliff | 4.56 | 5.67 | 6.78 |
The NULLs can be zero.
With a self join I am able to get Cliff, but unable to get Barry and April because i'm using something like this which only outputs if all three conditions are available.
SELECT a.Name, a.Amt Code_A, b.Amt Code_B, c.Amt Code_C
FROM Table_1 as c INNER JOIN
Table_1 AS b ON c.Name = b.Name INNER JOIN
Table_1 AS a ON b.Name = a.Name
WHERE (a.Code = 'A') AND (b.Code = 'B') AND (c.Code = 'C')
Instead of JOINs, I think a PIVOT is more appropriate here:
SELECT
Name,
[A] AS Code_A,
[B] AS Code_B,
[C] AS Code_C
FROM (
SELECT Name, Code, Amount
FROM Table_1
) t
PIVOT (
SUM(Amount)
FOR Code IN ([A], [B], [C])
) AS pvt
A completely sql engine agnostic way is:
select names.Name,
(select sum(a2.Amt) from amounts a2
where a2.Name = names.Name
and a2.Code = 'A') as AmtA,
(select sum(a3.Amt) from amounts a3
where a3.Name = names.Name
and a3.Code = 'B') as AmtB,
(select sum(a4.Amt) from amounts a4
where a4.Name = names.Name
and Code = 'C') as AmtC
from (select distinct Name from amounts) as names
Where you select the unique set of names, and then sum up amounts for each particular code in place. This is more intended to be instructional as to how SQL works.
In practice, I wouldn't actually use this in your case -- PIVOT is going to be much more efficient for any engine that supports it. As shown here: http://sqlfiddle.com/#!3/7cb0a/5