SQL paging with non-fixed-length page size - sql

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.

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

How to insert different values from same row in multiple rows of a new table

I have the A table:
id bigint,
a integer,
b integer,
c integer,
date date
and I have to put every single value in a new table following by the date, in this way:
B table:
id bigint,
type integer,
date date
For example, if in my A table I had a row like this:
id a b c date
13 5 4 7 2014-11-09
I would like to put this values in B table like this:
id type date
1 5 2014-11-09,
2 4 2014-11-09,
3 7 2014-11-09
Any suggestions?
Unpivot first the data using UNION ALL and then assign a new id using ROW_NUMBER:
SELECT
ROW_NUMBER() OVER(ORDER BY id, col) AS id,
type,
date
FROM (
SELECT id, 'a' AS col, a AS type, date FROM tableA UNION ALL
SELECT id, 'b' AS col, b AS type, date FROM tableA UNION ALL
SELECT id, 'c' AS col, c AS type, date FROM tableA
) t
If the new id is auto generated, you do not need the ROW_NUMBER at all.
SELECT a AS type, date FROM tableA UNION ALL
SELECT b AS type, date FROM tableA UNION ALL
SELECT c AS type, date FROM tableA
INSERT INTO A
( id ,
a ,
b ,
c ,
date )
VALUES ( 13,
5 ,
4 ,
7 ,
2014-11-09
)
ALTER TRIGGER NewTrigger ON A
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #id AS NVARCHAR(50)
DECLARE #type AS NVARCHAR(50)
DECLARE #date AS INT
SELECT #Date = INSERTED.Date
FROM INSERTED
INSERT INTO B
( Date,
Type,
id
)
VALUES ( #Date,
4,
1
)
END
GO
SELECT * A
SELECT * FROM B
In Oracle you can try using REGEXP to simulate the sqame . Hope this helps.
SELECT DISTINCT LEVEL,
TRIM(regexp_substr(a.colk,'[^,]+', 1, level)) TYPE,
SYSDATE
FROM
(SELECT LEVEL,
COL1
||','
||COL2
||','
||COL3 colk,
SYSDATE
FROM
(SELECT 13 AS ID,5 COL1,4 AS COL2,7 AS COL3,sysdate AS dt FROM DUAL
)
CONNECT BY LEVEL <= REGEXP_COUNT(COL1
||','
||COL2
||','
||COL3,',')+1
)a
CONNECT BY regexp_substr(a.colk, '[^,]+', 1, level) IS NOT NULL;
Contest for shortest code :) My variant is:
insert into b(type, date)
select unnest(array[a,b,c]), date from a;
Assuming that the id column in the b table is autoincremented.

Fixed array and random numbers in an SQL query

What's the easiest way to select one of five fixed strings returned in an SQL Server query, randomly?
I.e. the equivalent of:
function randomColumn() {
var values = ['apple', 'banana', 'orange', 'cherry', 'lemon'];
var idx = Math.floor(Math.random() * 5);
return values[idx];
}
I need to change my existing SQL script to have a certain column return one of these values, without the need to change my client code.
Do I need to create a temp table?
I'm using SQL Server 2008 R2.
You don't need a temporary table for a few strings, you can create the result on the fly:
select str
from (
select 0 as id, 'apple' as str
union all
select 1, 'banana'
union all
select 2, 'orange'
union all
select 3, 'cherry'
union all
select 4, 'lemon'
) x
where id = floor(rand() * 5)
select top 1 Value
from Table1
order by newid()
SQL Fiddle Example
Select 'apple' values
INTO #tmp
UNION ALL
Select'banana' values
UNION ALL
Select 'orange' values
.
.
.
select top 1 values
from #tmp
order by newid()
OR
ALTER TABLE #tmp
ADD id BIGINT IDENTITY(1,1)
DECLARE #rand BIGINT
SET #rand=rand()*4
select top 1 values
from #tmp
where id=#rand
DECLARE #ListofIDs TABLE(IDs VARCHAR(100), ID INT IDENTITY(1,1));
INSERT INTO #ListofIDs
SELECT 'a'
UNION ALL
SELECT 'b'
UNION ALL
SELECT '20'
UNION ALL
SELECT 'c'
UNION ALL
SELECT '30'
UNION ALL
SELECT 'd';
SELECT * FROM #ListofIDs;
SELECT Ids from #ListofIDs where ID=1+ CONVERT(INT, (5)*RAND())

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.