Find bus route number from sql queries - sql-server

I am designing bus route database. i have three tables like below,
LocationDetails
Location ID Location Name
---------------------------
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
RouteDetails
RouteId RouteNumber
--------------------
1 101
2 102
3 103
RouteLocationDetails
RouteId LocationId
------------------
1 3
1 4
1 5
1 6
2 2
2 3
2 4
2 5
2 6
I want to find the route numbers for the user entered location name.
For example if user want to travel the location from 'C' to 'F' they need to know what and all route number available for that locations, in my case 2 bus route number is available for that route (Routenumber 101, 102)
Can anyone tell me how to write the sql query for this?

If you only need to handle simple query like A to B, you can self join the RouteLocationDetails table
select r1.RouteId from RouteLocationDetails r1 join RouteLocationDetails r2 on r1.RouteId = r2.RouteId where r1.LocationId <> r2.LocationId and r1.LocationId = 'LocationNameOfC' and r2.LocationId = 'LocationNameOfF'
just a simple illustration, you need to join back the other two table for the necessary information.

You can use below and make sure to pass two location names...
select rd.RouteNumber,r.RouteId from #LocationDetails l
Join
#RouteLocationDetails r
on
l.[Location ID]=r.LocationId
join
#RouteDetails rd
on
r.RouteId=rd.RouteId
where [Location Name]='C'
or [Location Name]='F'
group by rd.RouteNumber,r.RouteId
having count(r.LocationId)=2
Output
RouteNumber RouteId
101 1
102 2

I've created the following SQL Fiddle which should work in your case: http://sqlfiddle.com/#!6/4824f/2
Creating the tables and inserting the data:
create table LocationDetails ([Location ID] int, [Location Name] varchar(1))
INSERT INTO LocationDetails ([Location ID],[Location Name]) VALUES(1, 'A')
INSERT INTO LocationDetails ([Location ID],[Location Name]) VALUES(2, 'B')
INSERT INTO LocationDetails ([Location ID],[Location Name]) VALUES(3, 'C')
INSERT INTO LocationDetails ([Location ID],[Location Name]) VALUES(4, 'D')
INSERT INTO LocationDetails ([Location ID],[Location Name]) VALUES(5, 'E')
INSERT INTO LocationDetails ([Location ID],[Location Name]) VALUES(6, 'F')
INSERT INTO LocationDetails ([Location ID],[Location Name]) VALUES(7, 'G')
INSERT INTO LocationDetails ([Location ID],[Location Name]) VALUES(8, 'H')
INSERT INTO LocationDetails ([Location ID],[Location Name]) VALUES(9, 'I')
create table RouteDetails (RouteId int, RouteNumber int)
INSERT INTO RouteDetails (RouteId, RouteNumber) VALUES(1, 101)
INSERT INTO RouteDetails (RouteId, RouteNumber) VALUES(2, 102)
INSERT INTO RouteDetails (RouteId, RouteNumber) VALUES(3, 103)
create table RouteLocationDetails (RouteId int, LocationId int)
INSERT INTO RouteLocationDetails (RouteId, LocationId) VALUES(1, 3)
INSERT INTO RouteLocationDetails (RouteId, LocationId) VALUES(1, 4)
INSERT INTO RouteLocationDetails (RouteId, LocationId) VALUES(1, 5)
INSERT INTO RouteLocationDetails (RouteId, LocationId) VALUES(1, 6)
INSERT INTO RouteLocationDetails (RouteId, LocationId) VALUES(2, 2)
INSERT INTO RouteLocationDetails (RouteId, LocationId) VALUES(2, 3)
INSERT INTO RouteLocationDetails (RouteId, LocationId) VALUES(2, 4)
INSERT INTO RouteLocationDetails (RouteId, LocationId) VALUES(2, 5)
INSERT INTO RouteLocationDetails (RouteId, LocationId) VALUES(2, 6)
Query the from and to location:
select RouteNumber from LocationDetails ldfrom
inner join RouteLocationDetails rldfrom on rldfrom.LocationId = ldfrom.[Location Id]
inner join RouteLocationDetails rldto on rldto.RouteId = rldfrom.RouteId
inner join LocationDetails ldto on ldto.[Location Id] = rldto.LocationId
inner join RouteDetails rd on rd.RouteId = rldto.RouteId
where ldfrom.[Location Name] = 'C' and ldto.[Location Name] = 'F'

Related

Perform join based on near by Dates

I have the following two tables and want to join based on the near by dates (most recent available date).
I can't use Delete/Inserts because I need to convert this into KQL(Kusto). Please help.
Code snippet:
drop table if exists #table1
drop table if exists #table2
create table #table1 ([date] date, val int)
create table #table2 ([date] date, discount int)
insert into #table1 ([date], val) values ('1/26/2010', 10)
insert into #table1 ([date], val) values ('1/25/2010', 9)
insert into #table1 ([date], val) values ('1/24/2010', 8)
insert into #table1 ([date], val) values ('1/24/2010', 9)
insert into #table1 ([date], val) values ('1/23/2010', 7)
insert into #table1 ([date], val) values ('1/22/2010', 10)
insert into #table1 ([date], val) values ('1/19/2010', 11)
insert into #table2 ([date], discount) values ('1/26/2010', 2)
insert into #table2 ([date], discount) values ('1/23/2010', 1)
insert into #table2 ([date], discount) values ('1/20/2010', 0)
Desired Result:
date val date discount
2010-01-26 10 2010-01-26 2
2010-01-23 7 2010-01-23 1
2010-01-19 11 2010-01-20 0
I got the answer but any efficient method that you can suggest are welcome:
select * from (
Select t2.date,
t2.discount,t1.date as date2, t1.val ,row_number() over( partition by t2.date order by t1.date desc) rn
from #table2 t2
inner join #table1 t1 on t1.date <= t2.date) t
where t.rn = 1

How to use FOR XML function prorperly in SQL Server for faster data extraction [duplicate]

This question already has answers here:
Comma separated results in SQL
(5 answers)
String_agg for SQL Server before 2017
(2 answers)
Closed 2 years ago.
I have below table like: SQL Fiddle
I am able to get this output via FOR XML, but I am not sure how I can get below output properly for larger number of users (approx 0.2M users).
Later I want to get top-3 Names by their counts for each id, so RANK or OrderBy clauses will come into SQL and not sure how many iteration will it take when data is of large no. users.
Screenshot of table after group by:
Required output:
Working code that I have tried:
-----------SQL Raw Table Creation------------------------
CREATE TABLE tb
(
Id INT,
Name VARCHAR(50) NOT NULL
);
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'bb');
INSERT INTO tb (Id, Name) VALUES (1, 'cc');
INSERT INTO tb (Id, Name) VALUES (1, 'cc');
INSERT INTO tb (Id, Name) VALUES (1, 'dd');
INSERT INTO tb (Id, Name) VALUES (1, 'dd');
INSERT INTO tb (Id, Name) VALUES (1, 'dd');
INSERT INTO tb (Id, Name) VALUES (2, 'aa');
INSERT INTO tb (Id, Name) VALUES (2, 'bb');
INSERT INTO tb (Id, Name) VALUES (2, 'bb');
INSERT INTO tb (Id, Name) VALUES (2, 'ee');
INSERT INTO tb (Id, Name) VALUES (2, 'ee');
INSERT INTO tb (Id, Name) VALUES (2, 'ee');
INSERT INTO tb (Id, Name) VALUES (2, 'ee');
INSERT INTO tb (Id, Name) VALUES (3, 'aa');
INSERT INTO tb (Id, Name) VALUES (3, 'bb');
INSERT INTO tb (Id, Name) VALUES (3, 'cc');
INSERT INTO tb (Id, Name) VALUES (3, 'dd');
INSERT INTO tb (Id, Name) VALUES (3, 'dd');
INSERT INTO tb (Id, Name) VALUES (3, 'dd');
-----------------Want to RANK or get only top 3 rows for each Id when group by Name--------------
select f.* into #t1
from(
select f.*
from(
select f.*
from (
select top 3 id,name,count(name) as total
from tb
where id = 1
group by id,name
order by id,total desc
)f
Union
select top 3 id,name,count(name) as total
from tb
where id = 2
group by id,name
order by id,total desc
)f
Union
select top 3 id,name,count(name) as total
from tb
where id = 3
group by id,name
order by id,total desc
) f
/* Output is moved in temp table #t1 which looks like
id name total
1 aa 5
1 cc 2
1 dd 3
2 aa 1
2 bb 2
2 ee 4
3 bb 1
3 cc 1
3 dd 3
*/
---------Final Joining for each Top3Names and RespectiveTotal -----
select a.id as ID, a.listStr as Top3Names , b.Total as RespectiveTotal
from
(
SELECT id,STUFF((SELECT ',' + name
FROM #t1 EE
WHERE EE.id=E.id
FOR XML PATH('')), 1, 1, '') AS listStr
FROM #t1 E
GROUP BY E.id
)a
left Join
(
SELECT id,STUFF((SELECT ',' + cast(total as Varchar)
FROM #t1 EE
WHERE EE.id=E.id
FOR XML PATH('')), 1, 1, '') AS Total
FROM #t1 E
GROUP BY E.id
)b
on a.id=b.id
Output:
ID Top3Names RespectiveTotal
1 aa,cc,dd 5,2,3
2 aa,bb,ee 1,2,4
3 bb,cc,dd 1,1,3
Also let me know any easy solution or alternatives so that I can test it on larger.

How to optimize the query so the manual work can be dyanmic and in optimized way

I have below table like: SQL fiddle
I am able to get this output via XML, but I am not sure how I can get below output properly for larger number of users (approx 0.2M users).
Later I want to get top-3 Names by their counts for each id ,so RANK or OrderBy clauses will come into SQL and not sure how many iteration will it take when data is of large number of users.
Working code that I have tried:
-----------SQL Raw Table Creation------------------------
CREATE TABLE tb
(
Id INT,
Name VARCHAR(50) NOT NULL
);
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'bb');
INSERT INTO tb (Id, Name) VALUES (1, 'cc');
INSERT INTO tb (Id, Name) VALUES (1, 'cc');
INSERT INTO tb (Id, Name) VALUES (1, 'dd');
INSERT INTO tb (Id, Name) VALUES (1, 'dd');
INSERT INTO tb (Id, Name) VALUES (1, 'dd');
INSERT INTO tb (Id, Name) VALUES (2, 'aa');
INSERT INTO tb (Id, Name) VALUES (2, 'bb');
INSERT INTO tb (Id, Name) VALUES (2, 'bb');
INSERT INTO tb (Id, Name) VALUES (2, 'ee');
INSERT INTO tb (Id, Name) VALUES (2, 'ee');
INSERT INTO tb (Id, Name) VALUES (2, 'ee');
INSERT INTO tb (Id, Name) VALUES (2, 'ee');
INSERT INTO tb (Id, Name) VALUES (3, 'aa');
INSERT INTO tb (Id, Name) VALUES (3, 'bb');
INSERT INTO tb (Id, Name) VALUES (3, 'cc');
INSERT INTO tb (Id, Name) VALUES (3, 'dd');
INSERT INTO tb (Id, Name) VALUES (3, 'dd');
INSERT INTO tb (Id, Name) VALUES (3, 'dd');
-----------------Want to RANK or get only top 3 rows for each Id when group by Name--------------
select f.* into #t1
from(
select f.*
from(
select f.*
from (
select top 3 id,name,count(name) as total
from tb
where id = 1
group by id,name
order by id,total desc
)f
Union
select top 3 id,name,count(name) as total
from tb
where id = 2
group by id,name
order by id,total desc
)f
Union
select top 3 id,name,count(name) as total
from tb
where id = 3
group by id,name
order by id,total desc
) f
/* Output is moved in temp table #t1 which looks like
id name total
1 aa 5
1 cc 2
1 dd 3
2 aa 1
2 bb 2
2 ee 4
3 bb 1
3 cc 1
3 dd 3
*/
---------Final Joining for each Top3Names and RespectiveTotal -----
select a.id as ID, a.listStr as Top3Names , b.Total as RespectiveTotal
from
(
SELECT id,STUFF((SELECT ',' + name
FROM #t1 EE
WHERE EE.id=E.id
FOR XML PATH('')), 1, 1, '') AS listStr
FROM #t1 E
GROUP BY E.id
)a
left Join
(
SELECT id,STUFF((SELECT ',' + cast(total as Varchar)
FROM #t1 EE
WHERE EE.id=E.id
FOR XML PATH('')), 1, 1, '') AS Total
FROM #t1 E
GROUP BY E.id
)b
on a.id=b.id
Output:
ID Top3Names RespectiveTotal
1 aa,cc,dd 5,2,3
2 aa,bb,ee 1,2,4
3 bb,cc,dd 1,1,3
Here I am using UNION for each ID, which is not correct way of doing. I want an optimized way. Also I am using a temp table to store my results. Is it a good way? Let me know for any correct solution or alternatives so that I can test it on larger.
on my SQL SERVER machine for given sample data, your query stats looks like this:
Total Logical Reads: 13
Total CPU Time: 00:00:00.007
if you are using SQL SERVER 2017+ you can use STRING_AGG function :
SELECT
id
, STRING_AGG(name,',') WITHIN GROUP (order by name asc) Top3Names
, STRING_AGG(countx,',') WITHIN GROUP (order by name asc) RespectiveTotal
FROM (
SELECT
id
, name
, count(*) countx
, ROW_NUMBER() over (partition by id order by count(*) desc) rownumber
FROM tb
GROUP BY name, id
) result1
WHERE
result1.rownumber < 4
GROUP BY id
Stats are like :
Total Logical Reads: 1
Total CPU Time: 00:00:00.000
for SQL SERVER 2016- :
select id
, STUFF((
SELECT ',' + t1.Name
FROM cte t1
WHERE t1.id = t2.id
and t1.rownumber < 4
ORDER BY t1.name
FOR XML PATH('')), 1, LEN(','), '') AS Top3Names
, STUFF((
SELECT ',' + cast(t1.countx as varchar(50))
FROM cte t1
WHERE t1.id = t2.id --and t1.name = t2.name
and t1.rownumber < 4
ORDER BY t1.name
FOR XML PATH('')), 1, LEN(','), '') AS RespectiveTotal
from cte t2
group by id
Stats are like :
Total Logical Reads: 7
Total CPU Time: 00:00:00.006
so regardless of sql server version, It will improve performance, you will get the best performance if you are using sql server 2017 or above using query above.

SQL Server: max of date

Table 1
RefId Name
----- ----
1 A
2 B
Table 2
RefId Date
----- -----
1 29/03/2018 07:15
1 29/03/2018 07:30
2 29/03/2018 07:35
2 29/03/2018 07:40
I would like the result to be as follows (Refid name and the max(date) from table 1 and 2 for that refid)
1 A 29/03/2018 07:30
2 B 29/03/2018 07:40
Query used
select
table1.refId, table1.name,
(select max(date) from table2)
from
table1, table2
where
table1.refid = table2.refid
group by
table2.refid
I am getting the following error message
Column is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Use JOIN and the aggregate function MAX with GROUP BY to select the max date for each RefId.
Query
select [t1].[RefId], [t1].[Name], max([t2].[date] as [date]
from [Table1] [t1]
join [Table2] [t2]
on [t1].[RefId] = [t2].[RefId]
group by [t1].[RefId], [t1].[Name];
'29/03/2018 07:15' is nvarchar-type, you need datetime.
nvarchar convert to datetime: SELECT CONVERT(datetime, '29/03/2018 07:15', 103)
Answer to your example:
DECLARE #Table1 TABLE(RefId int, Name nvarchar(10));
INSERT INTO #Table1(RefId, Name) VALUES(1, 'A'), (2, 'B');
DECLARE #Table2 TABLE(RefId int, [Date] nvarchar(50));
INSERT INTO #Table2(RefId, [Date])
VALUES
(1, '29/03/2018 07:15'),
(1, '29/03/2018 07:30'),
(2, '29/03/2018 07:35'),
(2, '29/03/2018 07:40');
SELECT t1.RefId, t1.Name, t2.Date
FROM #Table1 AS t1
INNER JOIN
(SELECT RefId, MAX(CONVERT(datetime, [Date], 103)) AS [Date]
FROM #Table2
GROUP BY RefId) AS t2
ON t1.RefId = t2.RefId

SQL Server query to take last grouped row by condition

I have simple SQL Server query:
declare #table table (id int, name nvarchar(5), deleted bit)
insert into #table(id, name, deleted) values(1, 'A1', 0)
insert into #table(id, name, deleted) values(2, 'A1', 0)
insert into #table(id, name, deleted) values(3, 'A1', 0)
insert into #table(id, name, deleted) values(4, 'A1', 1)
insert into #table(id, name, deleted) values(5, 'A2', 0)
insert into #table(id, name, deleted) values(6, 'A2', 0)
select
max(id) as id,
name
from #table
where deleted = 0
group by name
it returns to rows
id |name
--------------
3 |A1
6 |A2
but should return only one
id |name
--------------
6 |A2
since last (or max) id of A1 is deleted.
How to fix my query.
Thanks a lot.
Since you could have a third set like this:
insert into #table(id, name, deleted) values(7, 'A3', 1)
insert into #table(id, name, deleted) values(8, 'A3', 0)
I assume you want the following returned as well, since the highest id for that name was not marked as deleted:
id name
---- ----
8 A3
Then this query should do it:
;WITH x AS
(
SELECT id, name, deleted,
rn = ROW_NUMBER() OVER (PARTITION BY name ORDER BY id DESC)
FROM #table
)
SELECT id = MAX(id), name
FROM x
WHERE NOT EXISTS
(
SELECT 1 FROM x AS x2
WHERE name = x.name
AND deleted = 1 AND rn = 1
)
GROUP BY name;
If you only want rows returned where no row for a particular name was ever deleted, then it's slightly simpler:
SELECT id = MAX(id), name
FROM #table AS t
WHERE NOT EXISTS
(
SELECT 1 FROM #table
WHERE name = t.name AND deleted = 1
)
GROUP BY name;
Another approach: http://www.sqlfiddle.com/#!3/697d7/5
create table t(id int, name nvarchar(5), deleted bit)
insert into t(id, name, deleted) values(1, 'A1', 0)
insert into t(id, name, deleted) values(2, 'A1', 0)
insert into t(id, name, deleted) values(3, 'A1', 0)
insert into t(id, name, deleted) values(4, 'A1', 1)
insert into t(id, name, deleted) values(5, 'A2', 0)
insert into t(id, name, deleted) values(6, 'A2', 0)
with no_deletions as
(
select name
from t
group by name
having max(nullif(cast(deleted as int),0)) is null
)
select
max(id) as id,
name
from t
where name in (select name from no_deletions)
group by name
Output:
| ID | NAME |
-------------
| 6 | A2 |

Resources