Unique Count - TSQL - sql

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

Related

Select data from one table base on selection from another table in SQL

I have these 3 table
First contain the item with price on given dates
2nd is the table of items
3rd is the table of dates in which we want to show the price of 2nd table item on every date
if duration is not available on first table it should be 0
with myTable ( item,startdate,enddate,price) as
(
select 'AAAA' ,'16-3-2020','19-3-2020','50' union all
select 'AAAA' ,'16-4-2020','19-4-2020','70' union all
select 'BBB' ,'16-3-2020','19-3-2020','20' union all
select 'BBB' ,'16-4-2020','19-4-2020','90' union all
select 'CCC' ,'16-3-2020','29-3-2020','45' union all
select 'CCC' ,'16-4-2020','19-4-2020','120'
)
select item,startdate,enddate,price from myTable
GO
with itemTable ( item) as
(
select 'AAAA' union all
select 'BBB' union all
select 'CCC'
)
select item from itemTable
GO
with DateTable ( dateItem) as
(
select '16-3-2020' union all
select '19-4-2020' union all
select '20-3-2020'
)
select dateItem from DateTable
GO
and my desire result should be like this (above is dynamic data)
with mydesireTable (item, [16-3-2020],[19-4-2020],[20-3-2020]) as
(
select 'AAAA' ,'50','70','0' union all ---0 as its not on above data in duration
select 'BBB' ,'20','90','0' union all
select 'CCC' ,'45','120','45'
)
select item, [16-3-2020],[19-4-2020],[20-3-2020] from mydesireTable
I am not sure what to search for :) as i want to write query for it which return my desire table as data ( or as in temporary table )
One of many ways to do this. This is a static crosstab. You need to list out all the columns explicitly (twice)
If your columns are dynamic, you need to use a dynamic crosstab. You should also consider doing this in your "presentation" layer, i.e. excel or whatever you are handing this over in.
You should consider what you want when something in mytable appears against a bucket twice (this solution will add the prices)
with myTable ( item,startdate,enddate,price) as
(
select 'AAAA' ,CAST('2020-03-16' AS DATE),CAST('2020-03-19' AS DATE),50 union all
select 'AAAA' ,'2020-04-16','2020-04-19',70 union all
select 'BBB' ,'2020-03-16','2020-03-19',20 union all
select 'BBB' ,'2020-04-16','2020-04-19',90 union all
select 'CCC' ,'2020-03-16','2020-03-29',45 union all
select 'CCC' ,'2020-04-16','2020-04-19',120
),
itemTable ( item) as
(
select 'AAAA' union all
select 'BBB' union all
select 'CCC'
)
,DateTable ( dateItem) as
(
select CAST('2020-03-16' AS DATE) union all
select '2020-04-19' union all
select '2020-03-20'
)
SELECT item,
[2020-03-16],[2020-04-19], [2020-03-20]
FROM
(
select item, dateitem, price from myTable
inner join datetable on datetable.dateItem between mytable.startdate and myTable.enddate
) As Src
PIVOT
(
SUM(price)
FOR
dateitem IN ([2020-03-16],[2020-03-20],[2020-04-19])
) as P
IF OBJECT_ID('tempdb..#myTable', 'U') IS NOT NULL
DROP TABLE #myTable;
IF OBJECT_ID('tempdb..#itemTable', 'U') IS NOT NULL
DROP TABLE #itemTable;
IF OBJECT_ID('tempdb..#DateTable', 'U') IS NOT NULL
DROP TABLE #DateTable;
CREATE TABLE #myTable (
item VARCHAR(MAX) NOT NULL,
startdate DATE NOT NULL,
enddate DATE NOT NULL,
price INT NOT NULL DEFAULT(0)
);
INSERT #myTable (item, startdate, enddate, price) VALUES
('AAAA' ,CAST('2020-03-16' AS DATE),CAST('2020-03-19' AS DATE),50),
('AAAA' ,'2020-04-16','2020-04-19',70),
('BBB' ,'2020-03-16','2020-03-19',20),
('BBB' ,'2020-04-16','2020-04-19',90),
('CCC' ,'2020-03-16','2020-03-29',45),
('CCC' ,'2020-04-16','2020-04-19',120)
CREATE TABLE #itemTable (
item VARCHAR(MAX) NOT NULL
)
INSERT #itemTable (item) VALUES
('AAAA'),
('BBB'),
('CCC')
CREATE TABLE #DateTable (
dateItem DATE NOT NULL
)
INSERT #DateTable (dateItem) VALUES
(CAST('2020-03-16' AS DATE)),
(CAST('2020-04-19' AS DATE)),
(CAST('2020-03-20' AS DATE)),
(CAST('2020-03-21' AS DATE)),
(CAST('2021-03-21' AS DATE)),
(CAST('2022-03-21' AS DATE))
Declare #DynamicCol nvarchar(max),#DynamicColNull nvarchar(max)
,#Sql nvarchar(max)
SELECT #DynamicColNull=STUFF((SELECT DISTINCT ', '+'ISNULL('+QUOTENAME(dateItem),','+'''0'''+') As '+QUOTENAME(dateItem)
FROM #DateTable FOR XML PATH ('')),1,2,'')
SELECT #DynamicCol=STUFF((SELECT DISTINCT ', '+QUOTENAME(dateItem) FROM #DateTable FOR XML PATH ('')),1,2,'')
SET #Sql='SELECT [item], '+#DynamicColNull+' From
(
select item, dateitem, price from #myTable
inner join #datetable on #datetable.dateItem between #mytable.startdate and #myTable.enddate
)
AS Src
PIVOT
(
SUM(price) FOR [dateitem] IN ('+#DynamicCol+')
)AS Pvt'
PRINT #Sql
EXEC(#Sql)

SQL paging with non-fixed-length page size

The table is simple: It has 2 columns, id and type. The type can be 1 or 2.
Now I want the first 10 entries with type 1, in a sequence ordered by id, and if there are type 2 entries somewhere in between then they should be included in the result set too. What is the SQL query?
It's basically paging but the number of rows per page can vary so can't use OFFSET and LIMIT.
For SQL Server you can use CTE to make request for type 1 records once and then union with records of Type 2.
I selected only 5 rows to use less test data.
create table #TEST (Id int, Type int)
insert into #TEST
select 1,2 union
select 2,1 union
select 3,1 union
select 4,2 union
select 5,2 union
select 6,1 union
select 7,1 union
select 8,2 union
select 9,1 union
select 10,1 union
select 11,2 union
select 12,2 union
select 13,1 union
select 14,1 union
select 15,1
go
with list1 (Id, Type)
as
(
select *
from #Test
where Type = 1
order by Id
offset 0 rows fetch next 5 rows only
)
select *
from list1
union all
select *
from #Test
where Type = 2 and Id > (select min(Id) from list1) and Id < (select max(Id) from list1)
order by Id
Should look something like this:
DECLARE #limit int = 25, #offset int = 10
DECLARE #ret TABLE (
ID INT,
TYPEID INT)
INSERT INTO #ret
SELECT Id, TypeId
FROM Logs
WHERE TypeId = 1
ORDER BY id
LIMIT #limit OFFSET #offset;
INSERT INTO #ret
SELECT id, TypeId
FROM Logs
WHERE TypeId = 2
AND ID BETWEEN (SELECT MAX(ID) FROM #ret) AND (SELECT MIN(ID) FROM #ret)
SELECT *
FROM #ret
ORDER BY ID
You could collapse this into a single SQL statement using a UNION, but it would have to query for the same data (limit and offset) more than once.

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

return true if all the the records of first table exists in second table

i have two table:
declare #t1 table (id int)
declare #t2 table (id int)
insert into #t1
select 1 union select 3 union select 7
insert into #t2
select 1 union select 3 union select 7 union select 9 union select 4
select count(*) from #t1 t inner join #t2 t1 on t.id = t1.id
i get the result for above query as 3. i need true or false if all the records in t1 exists in t2.
this is a simplified example of the real table structure. the real tables may have millions of records, so please let me know some optimized way of doing it
SELECT CASE
WHEN EXISTS (SELECT id
FROM #t1
EXCEPT
SELECT id
FROM #t2) THEN 0
ELSE 1
END
declare #t1 table (id int)
declare #t2 table (id int)
insert into #t1
select 1 union select 3 union select 7
insert into #t2
select 1 union select 3 union select 7 union select 9 union select 4
if exists(
select id from #t2
except
select id from #t1
) print 'false'
else print 'all the records in t1 exists in t2'
Using exists (probably, it would be more efficient):
select
case
when not exists (select 1
from #t1 t1
where not exists(select 1 from #t2 t2 where t2.id = t1.id))
then cast(1 as bit)
else cast(0 as bit)
end
SELECT (CASE WHEN
(SELECT COUNT(*) from t1 where
not id IN (select id from t2)) = 0 THEN
convert(bit, 1)
ELSE convert(bit, 0) END)
Comparing count of matched rows to the total rows in #t1 may be more efficient. Sometimes you just need to try multiple methods and look at query plans to see which one works best in your situation. You'll need some test tables with a similar amount of data and proper indexes and such.
declare #t1 table (id int)
declare #t2 table (id int)
insert into #t1
select 1 union select 3 union select 7
insert into #t2
select 1 union select 3 union select 7 union select 9 union select 4
select case
when (select count(*) from #t1 t join #t2 t1 on t.id = t1.id) =
(select count(*) from #t1) then 1 else 0
end as rows_match

SQL grouping by parent child

If I had this structure with the columns:
Primary_Key, Name, Parent_Primary_ID, DISPLAY_ORDER
1 Event NULL 1
2 News NULL 2
3 Event_List 1 1
4 Event_Detail 1 2
5 News_List 2 1
6 News_Details 2 2
how would you return data like:
1 Event
3 Event_List
4 Event_Detail
2 News
5 News_List
6 News_Detail
Thanks
Rob
If SQL Server 2005+
DECLARE #YourTable TABLE
(Primary_Key INT PRIMARY KEY,
Name VARCHAR(100),
Parent_Primary_ID INT NULL,
DISPLAY_ORDER INT)
INSERT INTO #YourTable
SELECT 1,'Event',NULL,1 UNION ALL
SELECT 2,'News',NULL,2 UNION ALL
SELECT 3,'Event_List',1,1 UNION ALL
SELECT 4,'Event_Detail',1,2 UNION ALL
SELECT 5,'News_List',2,1 UNION ALL
SELECT 6,'News_Details',2,2;
WITH Hierarchy
AS (SELECT *,
path = CAST(DISPLAY_ORDER AS VARCHAR(100))
FROM #YourTable
WHERE Parent_Primary_ID IS NULL
UNION ALL
SELECT y.Primary_Key,
y.Name,
y.Parent_Primary_ID,
y.DISPLAY_ORDER,
CAST(path + '.' + CAST(y.DISPLAY_ORDER AS VARCHAR) AS VARCHAR(100))
FROM #YourTable y
JOIN Hierarchy h
ON h.Primary_Key = y.Parent_Primary_ID)
SELECT Primary_Key,
Name
FROM Hierarchy
ORDER BY path
Try (asumming standardish sql is supported)
DECLARE #YourTable TABLE
(Primary_Key INT PRIMARY KEY,
Name VARCHAR(100),
Parent_Primary_ID INT NULL,
DISPLAY_ORDER INT)
INSERT INTO #YourTable
SELECT 1,'Event',NULL,1 UNION ALL
SELECT 2,'News',NULL,2 UNION ALL
SELECT 3,'Event_List',1,1 UNION ALL
SELECT 4,'Event_Detail',1,2 UNION ALL
SELECT 5,'News_List',2,1 UNION ALL
SELECT 6,'News_Details',2,2;
select
primary_key = t1.primary_key,
name = t1.name
from
#YourTable t1
left join #YourTable t2 on t1.parent_primary_id = t2.Primary_Key
order by
coalesce(t2.DISPLAY_ORDER,t1.DISPLAY_ORDER,0),
case
when t2.Primary_Key is null then 0
else t1.DISPLAY_ORDER
end
I don't see any grouping in your results. Unless you are trying to do something you aren't telling us I would use the query below:
SELECT Primary_Key, Name FROM YourTable
I don't see how you are ordering those results so I didn't try to order them.