Nested SQL Query from multiple tables and COUNT inline - sql

Good day bloggers.
I want to create a query which will select car name, model, price from table tblCars. Within the same query result, I need to check from table tblLikes if certain user has liked the car. tblLikes has id, userId and carId (foreign key). So for a specific user I need something like the following in return.
1 Corolla Toyota R100,000 1 (user liked)
2 A3 Audi R700,000 0 (user did not like)
What Ι have tried is the following without success.
SELECT
CarName
, CarModel
, CarPrice
, (
SELECT COUNT(*)
FROM tblLikes
WHERE
userId = #userId
AND CarId = #CarID
) as Likes
FROM tblCars
SELECT COUNT(*)
FROM tblLikes
WHERE
userId = #userId
AND CarId = #CarID
Return 1 if the record exist and 0 if it does not.

declare #tblCars table (
id int IDENTITY(1,1) NOT NULL
, CarName varchar(20)
, CarModel varchar(20)
, CarPrice money
)
declare #tblLikes table (
id int IDENTITY(1,1) NOT NULL
, userId int
, carId int
)
insert into #tblCars (
CarName
, CarModel
, CarPrice
)
select 'BMW' , 'qqq', '1000'
union all select 'Mercedes' , 'www', '2000'
union all select 'Audi' , 'eee', '3000'
union all select 'Scania' , 'rrr', '4000'
union all select 'Opel' , 'ttt', '5000'
union all select 'Mazda' , 'yyy', '6000'
union all select 'Budatti' , 'uuu', '7000'
union all select 'Lamborgini' , 'iii', '8000'
union all select 'Tesla' , 'ooo', '9000'
union all select 'Space' , 'ppp', '10000'
insert into #tblLikes (
userId
, carId
)
select '1', '1'
union all select '1', '3'
union all select '1', '5'
union all select '1', '7'
union all select '1', '9'
select
c.*
, (
case when l.id is not null
then '1'
else '0'
end
) as likes
from #tblCars as c
left outer join #tblLikes as l
on l.carId = c.id

Related

Join two tables multiple times on same column with a lookup table in between

This has probably been answered but, its hard to search for this question, as you can see in my confusing title.
Anyhow, I hope this example will help:
The tricky part is the one to many relationship in the parameter lookup table.
Ive tried using multiple joins and aliases resulting in a hugh number of rows since Im getting every 'amount' for every 'price'.
SELECT paraval.month, paraval.value as amount, paraval2.value as price, trade.position
FROM trade
INNER JOIN parameter para on trade.tID=para.tID and para.name = 'amount'
INNER JOIN parametervalues paraval on para.pID=paraval.pID
INNER JOIN parameter para2 on trade.tID=para2.tID and para2.name = 'price'
INNER JOIN parametervalues paraval2 on para2.pID=paraval2.pID
WHERE trade.type = 'cert'
Guessing I need sub-queries, but not sure where to place them.
EDIT add some SQL code structure :
CREATE TABLE #Trade
(
tID int PRIMARY KEY,
type varchar(50),
position int
);
CREATE TABLE #Parameter
(
pID int PRIMARY KEY,
tID int,
name varchar(50)
);
CREATE TABLE #ParameterValue
(
pID int,
smonth varchar(50),
value varchar(50));
INSERT INTO #Trade
SELECT 1, 'stock', 1
UNION
SELECT 2, 'stock', 2
UNION
SELECT 3, 'cert', 3
INSERT INTO #Parameter
SELECT 1,1,'amount'
UNION
SELECT 2,1,'price'
UNION
SELECT 3,2,'amount'
UNION
SELECT 4,2,'price'
UNION
SELECT 5,3,'amount'
UNION
SELECT 6,3,'price'
INSERT INTO #ParameterValue
SELECT 1,1,'5'
UNION
SELECT 2,1,'500'
UNION
SELECT 3,1,'15'
UNION
SELECT 4,1,'300'
UNION
SELECT 5,1,'5'
UNION
SELECT 5,2,'10'
UNION
SELECT 5,3,'5'
UNION
SELECT 6,1,'100'
UNION
SELECT 6,2,'200'
UNION
SELECT 6,3,'300'
-- SELECT * FROM #Trade
-- SELECT * FROM #Parameter
-- SELECT * FROM #ParameterValue
DROP TABLE #Trade
DROP TABLE #Parameter
DROP TABLE #ParameterValue
I think the best way for build your excepted output and relevant schema you have to use pivot with dynamic sql because in next day it possible to have some new values it’s the principal of your structure.
But i think this query can be respond :
SELECT paraval.month, (case when para. name = 'amount' then max(paraval.value) else null end)as amount, (case when para. name = 'price' then max(paraval.value) else null end) as price, max(trade.position) as position
FROM trade
INNER JOIN parameter para on trade.tID=para.tID
INNER JOIN parametervalues paraval on para.pID=paraval.pID
WHERE trade.type = 'cert'
Group by paraval.month
EDIT correction off previous query :
CREATE TABLE #Trade
(
tID int PRIMARY KEY,
type varchar(50),
position int
);
CREATE TABLE #Parameter
(
pID int PRIMARY KEY,
tID int,
name varchar(50)
);
CREATE TABLE #ParameterValue
(
pID int,
smonth varchar(50),
value varchar(50));
INSERT INTO #Trade
SELECT 1, 'stock', 1
UNION
SELECT 2, 'stock', 2
UNION
SELECT 3, 'cert', 3
INSERT INTO #Parameter
SELECT 1,1,'amount'
UNION
SELECT 2,1,'price'
UNION
SELECT 3,2,'amount'
UNION
SELECT 4,2,'price'
UNION
SELECT 5,3,'amount'
UNION
SELECT 6,3,'price'
INSERT INTO #ParameterValue
SELECT 1,1,'5'
UNION
SELECT 2,1,'500'
UNION
SELECT 3,1,'15'
UNION
SELECT 4,1,'300'
UNION
SELECT 5,1,'5'
UNION
SELECT 5,2,'10'
UNION
SELECT 5,3,'5'
UNION
SELECT 6,1,'100'
UNION
SELECT 6,2,'200'
UNION
SELECT 6,3,'300'
/***/
-- Perform select
/***/
SELECT t.tID, paraval.smonth, MAX(case when para.name = 'amount' then paraval.value else null end)as amount, MAX(case when para.name = 'price' then paraval.value else null end) as price, max(T.position) as position
FROM #Trade T
INNER JOIN #Parameter para on T.tID=para.tID
INNER JOIN #ParameterValue paraval on para.pID=paraval.pID
Group by T.tId, paraval.smonth
/***/
DROP TABLE #Trade
DROP TABLE #Parameter
DROP TABLE #ParameterValue
RESULT :
tID smonth amount price position
1 1 5 500 1
2 1 15 300 2
3 1 5 100 3
3 2 10 200 3
3 3 5 300 3

Unique Count - TSQL

CODE
CREATE TABLE #TEMP (ID INT, AVAIL BIT, FK INT, DT DATETIME);
INSERT INTO #TEMP (ID,AVAIL,FK,DT)
SELECT 1,1,1,GETDATE()
UNION ALL
SELECT 2,0,2,GETDATE()
UNION ALL
SELECT 3,1,3,GETDATE()
UNION ALL
SELECT 1,1,4,GETDATE()
UNION ALL
SELECT 4,0,5,GETDATE()
UNION ALL
SELECT 5,1,6,GETDATE();
CREATE TABLE #FK (FK INT, DT2 DATETIME)
INSERT INTO #FK (FK, DT2)
SELECT 1,NULL
UNION
SELECT 2,DATEADD(DAY,1,GETDATE())
UNION
SELECT 3,DATEADD(DAY,1,GETDATE())
UNION
SELECT 4,NULL
UNION
SELECT 5,NULL
UNION
SELECT 6,DATEADD(DAY,1,GETDATE())
UNION
SELECT 7,DATEADD(DAY,1,GETDATE())
SELECT
[TotalIds] = COUNT(DISTINCT ID)
,[TotalAvail] = SUM(CASE WHEN [AVAIL] = 1 THEN 1 ELSE 0 END)
,[DTDIFF] = SUM(DATEDIFF(DAY,T1.DT,F.DT2))
FROM #TEMP T1 INNER JOIN #FK F
ON T1.FK = F.FK;
DROP TABLE #TEMP;
DROP TABLE #FK;
OUTPUT
TotalIds TotalAvail DTDIFF
5 4 3
DESIRED OUTPUT
TotalIds TotalAvail DTDIFF
5 3 3
GOAL:
I want to get sum/count of UNIQUE IDs where [AVAIL] = 1.
I can do that by COUNT(DISTINCT ID) WHERE [AVAIL] = 1 BUT... I need to do that within this SUM since I'm querying other data within the same query.
Desired output = 3
(for ID 1, 3, and 5).
Updated with Current/Desired output.
Updated with more data.
You could change UNION ALL for UNION and remove the duplicates
But you mention otherValue, so maybe you need something like this
SELECT SUM(otherValue)
FROM (
SELECT DISTINCT ID, AVAIL, otherValue
FROM TEMP
WHERE [AVAIL] = 1
) T
CREATE TABLE #TEMP (ID INT, AVAIL BIT, FK INT, DT DATETIME);
INSERT INTO #TEMP (ID,AVAIL,FK,DT)
SELECT 1,1,1,GETDATE()
UNION ALL
SELECT 2,0,2,GETDATE()
UNION ALL
SELECT 3,1,3,GETDATE()
UNION ALL
SELECT 1,1,4,GETDATE()
UNION ALL
SELECT 4,0,5,GETDATE()
UNION ALL
SELECT 5,1,6,GETDATE();
CREATE TABLE #FK (FK INT, DT2 DATETIME)
INSERT INTO #FK (FK, DT2)
SELECT 1,NULL
UNION
SELECT 2,DATEADD(DAY,1,GETDATE())
UNION
SELECT 3,DATEADD(DAY,1,GETDATE())
UNION
SELECT 4,NULL
UNION
SELECT 5,NULL
UNION
SELECT 6,DATEADD(DAY,1,GETDATE())
UNION
SELECT 7,DATEADD(DAY,1,GETDATE())
SELECT
[TotalIds] = COUNT(DISTINCT ID)
,[TotalAvail] = COUNT(DISTINCT CASE WHEN [AVAIL] = 1 THEN ID ELSE NULL END)
,[DTDIFF] = SUM(DATEDIFF(DAY,T1.DT,F.DT2))
FROM #TEMP T1 INNER JOIN #FK F
ON T1.FK = F.FK;
DROP TABLE #TEMP;
DROP TABLE #FK;
Use the cte result for your further process.
;WITH CTE_Temp AS
(SELECT COUNT(DISTINCT ID) [TotalAvail]
FROM #TEMP
WHERE [Avail]=1)
SELECT [TotalAvail]
FROM CTE_Temp

Summary table generation in SQL Server

I have a table like this:
I want to see data in a summary view like this:
I need help for a T-SQL script. Thank you.
Sorry for my little English.
This works:
[Setup]
CREATE TABLE #PaymentTable ( Id INT IDENTITY, AccountGroupId INT, AccountId INT, Payment INT )
INSERT INTO #PaymentTable ( AccountGroupId, AccountId, Payment )
SELECT 1, 1, 5 UNION ALL SELECT 1, 1, 5 UNION ALL
SELECT 1, 2, 5 UNION ALL SELECT 2, 4, 5 UNION ALL
SELECT 2, 3, 5 UNION ALL SELECT 2, 3, 5 UNION ALL
SELECT 2, 4, 5
CREATE TABLE #Group ( AccountGroupId INT, GroupName VARCHAR(100) )
INSERT INTO #Group ( AccountGroupId, GroupName )
SELECT 1, 'Group 1' UNION Select 2, 'Group 2'
CREATE TABLE #Account ( AccountId INT, AccountName VARCHAR(100) )
INSERT INTO #Account ( AccountId, AccountName )
SELECT 1, 'John' UNION Select 2, 'Edvard' UNION
SELECT 3, 'David' UNION SELECT 4, 'Jimi'
[Query]
SELECT
[Group],
Account,
TotalPayment
FROM
(
SELECT
#Group.AccountGroupId AS GroupId,
GroupName AS [Group],
'' AS Account,
SUM( Payment ) AS TotalPayment,
0 AS InnerOrder
FROM
#PaymentTable,
#Group
WHERE
#Group.AccountGroupId = #PaymentTable.AccountGroupId
GROUP BY
#Group.AccountGroupId,
#Group.GroupName
UNION
SELECT
AccountGroupId AS GroupId,
'' AS [Group],
AccountName AS Account,
SUM( Payment ) AS TotalPayment,
1 AS InnerOrder
FROM
#PaymentTable,
#Account
WHERE
#Account.AccountId = #PaymentTable.AccountId
GROUP BY
AccountGroupId,
AccountName
) AS inner_query
ORDER BY
GroupId,
InnerOrder,
Account

SQL How can I order the data using distinct?

I have the following Table 'tbl1'.
declare #tbl1 table ( col1 varchar(32))
insert into #tbl1
values ( 'C1' )
, ( 'B1' )
, ( 'X1' )
, ( 'A1' )
, ( 'B1' )
, ( 'C1' )
, ( 'B1' )
, ( 'A1' )
, ( 'X1' )
, ( 'C1' )
, ( 'D1' )
I tried the following query
select distinct col1
from #tbl1
order by col1
The output should come in the following order, and remove all the duplicate value
C1
B1
X1
A1
D1
You need to specify the order for your items, as others have noted.
Also you need a subquery and grouping to be able to sort over the minimal order column.
declare #source table (id int not null identity primary key, name nvarchar(3));
insert into #source
values ( 'C1' )
, ( 'B1' )
, ( 'X1' )
, ( 'A1' )
, ( 'B1' )
, ( 'C1' )
, ( 'B1' )
, ( 'A1' )
, ( 'X1' )
, ( 'C1' )
, ( 'D1' )
;
with grouped as
(
select min(id) as minId, name from #source
group by name
)
select name from grouped order by minId;
The query could be rewritten without CTE:
select grouped.name from
(select min(id) as minId, name from #source group by name) grouped
order by grouped.minId;
This yields exactly the result you requested.
To repeat what others have pointed out in comments: if you specify no ORDER, then the order of results is not guaranteed. The fact that you get your results in a certain order currently should be treated as coincidental. If you want a certain ordering in your results, you have to be explicit about it!
As an interesting note, in my experience this is especially important if you're doing a DISTINCT in your query, because depending on the statistics for your tables, the engine may or may not decide that ordering the data to execute the DISTINCT is in fact the best possible plan.
Given that you mention a very explicit ordering requirement...
The output should come in the following order, and remove all the duplicate value
C1
B1
X1
A1
D1
...you should make that explicit in your query:
SELECT DISTINCT
*,
CASE
WHEN Col1 = 'C1' THEN 0
WHEN Col1 = 'B1' THEN 1
WHEN Col1 = 'X1' THEN 2
WHEN Col1 = 'A1' THEN 3
WHEN Col1 = 'D1' THEN 4
ELSE 5
END AS SortColumn
FROM tbl1
ORDER BY SortColumn
(extension of Jeroen response)
If your "key" values are dynamic, you normally should have a separate table with the order of the keys...
declare #sort table (id varchar(10), ord int)
insert into #sort
values ( 'C1', 1 )
, ( 'X1', 2 )
, ( 'B1', 3 )
, ( 'A1', 4 )
, ( 'D1', 5 )
then you join/subquery on that table to calculate the SortColumn
-- join
SELECT DISTINCT
t.*,
s.ord SortColumn
FROM #tbl1 t
LEFT JOIN #sort s ON S.id = t.col1
ORDER BY SortColumn
-- subquery
SELECT DISTINCT
t.*,
(SELECT s.ord FROM #sort s WHERE s.id = t.col1) SortColumn
FROM #tbl1 t
ORDER BY SortColumn
declare #tbl1 table ( col1 varchar(32))
insert into #tbl1
values ( 'Basic Salary' )
, ( 'HRA' )
, ( 'OtherAllowance' )
, ( 'PF' )
, ( 'Basic Salary' )
, ( 'HRA' )
, ( 'Other Allowance' )
, ( 'PF' )
, ( 'ESIC' )
, ( 'Basic Salary' )
, ( 'HRA' )
, ( 'Other Allowance' )
, ( 'PF' )
, ( 'Basic Salary' )
select distinct col1
from #tbl1
order by col1

Generate sequence number comparing adjacent rows in TSQL

I need help in generating sequence number when group name changes in adjacent rows. I already tried DENSE RANK but it did not work.
Group || Sequence Number
========================
A || 1 7/1/2012
A || 2 7/2/2012
A || 3 7/2/2012
B || 1 7/3/2012
B || 2 7/3/2012
B || 3 7/3/2012
A || 1 7/4/2012
A || 2 7/5/2012
A || 3 7/5/2012
C || 1
B || 1
B || 2
C || 1
C || 2
Thanks
Here's a couple of solutions - one simple, one more complex but closer matching your question:
--if you want all As grouped first, then all Bs, etc
select *
, ROW_NUMBER() over (partition by [group] order by id) SequenceNumber
from demo
--if you want the more complex solution where the different groups of As are kept apart from one another
select id
, [group]
, ROW_NUMBER() over (partition by x.p order by x.id) sequenceNumber
from (
select id
, [group]
, (
select min(b.id)
from demo b
where b.[group] <> a.[group]
and b.id > a.id
) p
from demo a
) x
order by id
Code to setup / run the above sample:
create table demo
(
id bigint identity(1,1) not null primary key clustered
, [group] nchar not null
)
go
insert demo
select 'A'
union all select 'A'
union all select 'A'
union all select 'B'
union all select 'B'
union all select 'B'
union all select 'C'
union all select 'C'
union all select 'C'
union all select 'A' --in your example you seemed to alow a second group of As separate to the first
union all select 'A'
union all select 'A'
union all select 'A'
union all select 'C'
go
This should work, you can do a while loop.
declare #t table (
id int identity primary key,
yourgroup char,
grouprank int
);
insert into #t (yourgroup)
select yourgroup
from yourtable;
declare #lastgroup char,
#newrank int,
#i int = (select MIN(id) from #t),
#end int = (select MAX(id) from #t);
while #i <= #end begin
if #lastgroup = (select yourgroup
from #t
where id = #i) begin
set #newrank += 1;
end else begin
set #newrank = 1;
end;
select #lastgroup = yourgroup
from #t
where id = #i;
update #t
set grouprank = #newrank
where id = #i;
set #i += 1;
end;
select * from #t;
Sorry for the slow reply to your last comment; I've been at work/away for the start of the weekend. What you're after can be achieved based on my previous answer, but I suspect the code below would be much more efficient / readable. The drawback of the below code is that this does rely on the new SQL 2012 LAG and LEAD features.
You can read up on these features here: http://blog.sqlauthority.com/2011/11/15/sql-server-introduction-to-lead-and-lag-analytic-functions-introduced-in-sql-server-2012/
Info on SQL 2012 licensing here, should you choose to upgrade: http://www.microsoft.com/sqlserver/en/us/get-sql-server/how-to-buy.aspx
Obviously there are many reasons why upgrading may not be justifiable, but thought I'd provide this answer in case it's an option available to you / others looking for this solution:
--Sample Data Setup:
if object_id('demo') is not null drop table demo
go
create table demo
(
id bigint identity(1,1) not null primary key clustered
, groupId nchar not null
, startDate date not null constraint uk_demo_startDate unique
)
go
insert demo
select 'A', '2009-01-01'
union all select 'A', '2009-01-02'
union all select 'A', '2009-02-01'
union all select 'B', '2009-03-01'
union all select 'B', '2009-04-01'
union all select 'B', '2009-05-01'
union all select 'C', '2009-06-01'
union all select 'C', '2009-07-01'
union all select 'C', '2009-08-01'
union all select 'A', '2009-09-01'
union all select 'A', '2009-10-01'
union all select 'A', '2009-11-01'
union all select 'A', '2009-12-01'
union all select 'C', '2010-01-01'
union all select 'D', '2010-01-02'
union all select 'D', '2010-01-03'
union all select 'D', '2010-01-04'
union all select 'E', '2010-01-05'
union all select 'E', '2010-01-06'
union all select 'D', '2010-01-07'
union all select 'D', '2010-01-08'
union all select 'E', '2010-01-09'
union all select 'E', '2010-01-10'
union all select 'D', '2011-01-01'
union all select 'D', '2011-01-02'
union all select 'E', '2012-01-01'
union all select 'X', '2012-01-02'
union all select 'D', '2012-01-03'
go
--Actual Solution
select *
, noDays + noDaysAtStatusAtStart noDaysAtStatusAtEnd
from
(
select id
, groupId
, startDate
, noDays
, case
when groupId = previousGroupId then lag(noDays,1) over (order by startDate)
--when previousGroupId is null then 0 --covered by else
else 0
end noDaysAtStatusAtStart
from
(
select id
, startDate
, groupId
, endDate
, previousGroupId
, dateDiff(day,startDate,endDate) noDays
from
(
select id
, startDate
, groupId
, lead(startDate,1) over (order by startDate) endDate
, lag(groupId,1) over (order by startDate) previousGroupId
from demo
) x
) y
) z
order by z.startDate