How to do a proper view? - sql

I have two tables here:
Table1
Version | Position | PIN
1 | 1 | 1111
1 | 2 | 2222
2 | 1 | 3333
2 | 2 | 4444
I have another table
Table2
Name | Version | Position1 | Position2
'Phone1' | 1 | A | B
'Phone2' | 2 | C | D
Actually Table2.Position1 = (Table1.Position=1) and Table2.Position2 = (Table1.Position=2). Also Table1.Version = Table2.Version.
I would like to make the view that looks like this
Table3
Name | P_Name | PIN
'Phone1' | A | 1111
'Phone1' | B | 2222
'Phone2' | C | 3333
'Phone2' | D | 4444

Edited:
WITH CTE(Name, Version, Pos, Position)
AS (
SELECT Name, Version, Position1, 1 FROM Table2
UNION ALL
SELECT Name, Version, Position2, 2 FROM Table2
)
SELECT T2.Name, T2.Pos, T1.PIN
FROM Table1 t1
JOIN CTE t2
ON t1.Version = t2.Version
AND t1.Position = t2.Position;
Output:
| NAME | POS | PIN |
|--------|-----|------|
| Phone1 | A | 1111 |
| Phone1 | B | 2222 |
| Phone2 | C | 3333 |
| Phone2 | D | 4444 |
See this SQLFiddle
Earlier
You can do this using JOIN like this:
SELECT t2."Name", t1."Position", t1."PIN"
FROM Table1 AS t1
JOIN Table2 AS t2
ON t1."Version" = t2."Version"
AND ( (t1."Position" = 1 AND t2."Position1" = t1."PIN")
OR (t1."Position" = 2 AND t2."Position2" = t1."PIN")
)
Output:
| NAME | POSITION | PIN |
|--------|----------|------|
| Phone1 | 1 | 1111 |
| Phone1 | 2 | 2222 |
| Phone2 | 1 | 3333 |
| Phone2 | 2 | 4444 |
See this SQLFIddle

I think that most elegant and scalable way is to do this:
with cte(Name, Version, P_Name, Position) as (
select Name, Version, Position1, 1 from Table2
union all
select Name, Version, Position2, 2 from Table2
)
select
T2.Name, T2.P_Name, T1.PIN
from Table1 as T1
left outer join cte as T2 on
T2.Position = T1.Position and T2.Version = T1.Version
order by Name, P_Name
=> sql fiddle demo
You can easily add position = 3, 4 and then just add lines into cte

Related

Take the row after the specific row

I have the table, where I need to take the next row after the row which has course 'TA' and flag = 1. For this I created the column rnum (OVER DATE) which may help for finding it
| student | date | course | flag | rnum |
| ------- | ----- | ----------- | ---- | ---- |
| 1 | 17:00 | Math | null | 1 |
| 1 | 17:10 | Python | null | 2 |
| 1 | 17:15 | TA | 1 | 3 |
| 1 | 17:20 | English | null | 4 |
| 1 | 17:35 | Geography | null | 5 |
| 2 | 16:10 | English | null | 1 |
| 2 | 16:20 | TA | 1 | 2 |
| 2 | 16:30 | SQL | null | 3 |
| 2 | 16:40 | Python | null | 4 |
| 3 | 19:05 | English | null | 1 |
| 3 | 19:20 | Literachure | null | 2 |
| 3 | 19:30 | TA | null | 3 |
| 3 | 19:40 | Python | null | 4 |
| 3 | 19:50 | Python | null | 5 |
As a result I should have:
| student | date | course | flag | rnum |
| ------- | ----- | ------- | ---- | ---- |
| 1 | 17:20 | English | null | 4 |
| 2 | 16:30 | SQL | null | 3 |
There are many ways to get your desired result, let's see some of them.
1) EXISTS
You can use the EXISTS clause, specifying a subquery to match for the condition.
SELECT T2.*
FROM #MyTable T2
WHERE EXISTS (
SELECT 'x' x
FROM #MyTable T1
WHERE T1.course = 'TA' AND T1.flag = 1
AND T1.student = T2.student AND T2.rnum = T1.rnum + 1
)
2) LAG
You ca use window function LAG to access previous row for a given order and then filter your resultset with your conditions.
SELECT w.student, w.date, w.course, w.flag, w.rnum
FROM (
SELECT T1.*
, LAG(course, 1) OVER (PARTITION BY student ORDER BY rnum) prevCourse
, LAG(flag, 1) OVER (PARTITION BY student ORDER BY rnum) prevFlag
FROM #MyTable T1
) w
WHERE prevCourse = 'TA' AND prevFlag = 1
3) JOIN
You can self-JOIN your table on the next rnum and keep only the rows who match the right condition.
SELECT T2.*
FROM MyTable T1
JOIN MyTable T2 ON T1.student = T2.student AND T2.rnum = T1.rnum + 1
WHERE T1.course = 'TA' AND T1.flag = 1
4) CROSS APPLY
You can use CROSS APPLY to specify a subquery with the matching condition. It is pretty similar to EXISTS clause, but you will also get in your resultset the columns from the subquery.
SELECT T2.*
FROM #MyTable T2
CROSS APPLY (
SELECT 'x' x
FROM #MyTable T1
WHERE T1.course = 'TA' AND T1.flag = 1
AND T1.student = T2.student AND T2.rnum = T1.rnum + 1
) x
5) CTE
You can use common table expression (CTE) to extract matching rows and then use it to filter your table with a JOIN.
;WITH
T1 AS (
SELECT student, rnum
FROM #MyTable T1
WHERE T1.course = 'TA' AND T1.flag = 1
)
SELECT T2.*
FROM #MyTable T2
JOIN T1 ON T1.student = T2.student AND T2.rnum = T1.rnum + 1
Adding the rownumber was a good start, you can use it to join the table with itself:
WITH matches AS (
SELECT
student,
rnum
FROM table
WHERE flag = 1
AND course = 'TA'
)
SELECT t.*
FROM table t
JOIN matches m
on t.student = m.student
and t.rnum = m.rnum + 1

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

Linking Table to Itself and Getting Relational ID

I want to get accounts that have same id as other accounts and then ultimately figure out which account it s related to (see table below for example).
Table Structure
Account ID | flag | id2
123 | Y | 1
456 | N | 1
789 | N | 1
888 | Y | 2
999 | N | 2
Results I want:
Account ID | id2 | src_account_id
456 | 1 | 123
789 | 1 | 123
999 | 2 | 888
Here's the query that I have
Select account_id, id2, src_account_id
FROM table1
WHERE id2 IN (Select id2 FROM table1 WHERE flag = 'Y')
But I'm stuck with how to get src_account_id. I'm fairly sure it involves doing an inner join the table to itself, but I'm still not sure how to get the src_account_id.
You can try this. use a subquery to get flag = 'Y' result set. then self-join
SELECT t1.AccountID,t1.id2,t2.AccountID
FROM T t1 inner join (
SELECT id2,AccountID
FROM T
WHERE flag = 'Y'
) t2 on t1.id2 = t2.id2
WHERE t1.flag = 'N'
sqlfiddle
[Results]:
| AccountID | id2 | AccountID |
|-----------|-----|-----------|
| 456 | 1 | 123 |
| 789 | 1 | 123 |
| 999 | 2 | 888 |
Self join the table on id2 and flag.
SELECT t1."Account ID",
t1.id2,
t2."Account ID" src_account_id
FROM elbat t1
INNER JOIN elbat t2
ON t2.id2 = t1.id2
AND t1.flag = 'N'
AND t2.flag = 'Y';

Access SQL: Select from table if ID is in another

Say I have the table T1:
ID | PNo | MM | CP | Flag | Name |
---|-----|------|----|------|------|
1 | 13 | True | 4 | A | X |
1 | 92 | True | 3 | A | X |
2 | 1 | True | 3 | B | Y |
2 | 13 | False| 2 | A | Y |
3 | 13 | True | 3 | B | W |
4 | 1 | True | 3 | B | Z |
And T2:
ID | PNo | MM | CP |
---|-----|------|----|
1 | 13 | True | 4 |
2 | 92 | True | 3 |
3 | 1 | True | 3 |
4 | 13 | False| 2 |
5 | 13 | True | 3 |
1 | 1 | False| 3 |
What I want to do is to do a INSERT INTO where I take values of T1 and T2 but only if the ID of T2 is in T1 and if T1 has the flag with the value A.
I have tried two things:
1) INNER JOIN: Something like
INSERT INTO T3 (ID, PNo, MM, CP, Flag, Name)
SELECT T1.ID, T2.PNo, T2.MM, T2.CP, 'A', T1.Name
FROM T2 INNER JOIN T1 ON T1.ID = T2.ID
WHERE (T1.FLAG = 'A')
The problem here is that it literally takes every combination of all relevant rows from T1 and T2. What I want is, I only want to take those rows of T2 whose IDs are also in T1.
2) IN?
INSERT INTO T3 (ID, PNo, MM, CP, Flag, Name)
SELECT T1.ID, T2.PNo, T2.MM, T2.CP, 'A', T1.Name
FROM T2, T1
WHERE T2.ID IN
(SELECT ID FROM T1 WHERE Flag = 'X')
Problem here is, this takes foreeeeeeeeeeever!
Is there not a more sophisticated method for this?
edit:// Changed a value in T2 so that the example is more meaningful.
So what I want in the new table T3 is:
ID | PNo | MM | CP | Flag | Name |
---|-----|------|----|------|------|
1 | 13 | True | 4 | A | X |
1 | 1 | False| 3 | A | X |
2 | 1 | True | 3 | A | Y |
What I get instead is:
ID | PNo | MM | CP | Flag | Name |
---|-----|------|----|------|------|
1 | 13 | True | 4 | A | X |
1 | 1 | False| 3 | A | X |
1 | 13 | True | 4 | A | X |
1 | 1 | False| 3 | A | X |
2 | 1 | True | 3 | A | Y |
So basically for all T1 values that I select (ID, Name) and for all corresponding rows that I can match by ID in T2, I get every combination.
Your second solution is almost right, however, you perform cartesian product unnecessarily (I'm not surprised that it takes forever). Try this one and if it is slow then create an index on Flag.
INSERT INTO T3 (ID, PNo, MM, CP, Flag, Name)
SELECT *
FROM T2
WHERE T2.ID IN
(SELECT ID FROM T1 WHERE Flag = 'X')
This should do what you want:
INSERT INTO T3 (ID, PNo, MM, CP, Flag, Name)
SELECT T2.ID, T2.PNo, T2.MM, T2.CP, ?, "A"
FROM T2
WHERE T2.ID IN (SELECT ID FROM T1 WHERE Flag = "A");
You are selecting T1.ID in your first query. You can take either T1.ID or T2.ID, because you are requiring that they be equal.
You can do this with a JOIN:
INSERT INTO T3 (ID, PNo, MM, CP, Flag, Name)
SELECT T2.ID, T2.PNo, T2.MM, T2.CP, T1.Name, "A"
FROM T2 INNER JOIN
T1
ON T2.ID = T1.ID;
If this is generating duplicates, then you have multiple rows in T1 for a given name.

How can I write a select statement for this use case?

Please help me compose a SELECT statement. I have these two tables:
Table1 Table2
---------------- ------------------------------------------------
ID | PName | | ID | NameID | DateActive | HoursActive |
---------------- ------------------------------------------------
1 | Neil | | 1 | 1 | 8/2/2013 | 3 |
2 | Mark | | 2 | 1 | 8/3/2013 | 4 |
3 | Onin | | 3 | 2 | 8/2/2013 | 2 |
---------------- | 4 | 2 | 8/6/2013 | 5 |
| 5 | 3 | 8/7/2013 | 1 |
| 6 | 3 | 8/8/2013 | 10 |
------------------------------------------------
And I just want to retrieve the earliest DateActive but no duplicate PName. Like this:
PName | DateActive | HoursActive |
----------------------------------------
Neil | 8/2/2013 | 3 |
Mark | 8/2/2013 | 2 |
Onin | 8/7/2013 | 1 |
----------------------------------------
Something like this might do it. You need to find the min date for each NameID first, then join back to the table to get the hours.
SELECT
PName, MaxDate as DataActive, HoursActive
From
Table1 t1
inner Join Table2 t2 on t1.ID = t2.NameID
Inner Join (Select min(DateActive) as mindate, NameID from Table2 Group by NameID) as t3 on t3.mindate = t2.ActiveDate and t3.NameID = t2.NameId
This should be a pretty standard solution:
select t.pname,
t2.dateactive,
t2.hoursac
from table1 t
join table2 t2 on t.id = t2.nameid
join (
select nameid, min(dateactive) mindateactive
from table2
group by nameid
) t3 on t2.nameid = t3.name
and t3.mindateactive = t2.dateactive
If you are using an RDBMS that supports partition by statements, then this would be more efficient:
select pname, dateactive, HoursActive
from (
select t.pname,
t2.dateactive,
t2.hoursactive,
rank() over (partition by t.id order by t2.dateactive) rownum
from table1 t
join table2 t2 on t.id = t2.nameid
) t
where rownum = 1