Sql server - dealing with new data? - sql-server

A rock band has currently 100 songs :
select count(songName) from bands where name='Beatles'
result : 100.
I display those songs in my app via paging ( each time - 10 results)
How do I get the relevant page and its rows ?
like this : (SP)
declare #pageItems int=10
declare #pagenum int=1
select * from (
SELECT [id] , row_number() over ( order by songName) as n
FROM Bands where name='Beatles'
) a
where a.n > (#pagenum-1)*#pageItems and a.n<= (#pagenum)*#pageItems
But there is a problem.
Suppose a user is at page 3.
And the beatles publish a new song named : "aaa"
So now , there will be a mismatch because there is a new row which is inserted at the top ( and pushes all rows below).
I dont want to get all the rows into my app.
What is the correct way / (feature?) to get the first criteria results ?
I could use a temp table but it will be only for the current connection. ( and each time a user press "next" at paging - it's a new session).

This is a common problem in paging, whether or not to adjust to data that is moving. Usually your users won't notice, or care, that the total number of records changes during pagination or if a record that was at the bottom of page 2 suddenly is at the top of page 3 because a new record was added. But, if you really need the consistency, then if the dataset is small enough you can cache the entire list of PK's if a temp table or in your apps memory and then just join to that when fetching.
-> What is the best way to paginate results in SQL Server

Related

how to return null values from a set of data to show in SQL

So if I'm giving a set of data, and a small part of data are not being inserted into the database, how do I show them as actual "records" in SQL server?
select *
from cleansedcarddatabase
where clientcreditcards in (
'1235980878',
'1235980918',
'1235980969',
'1235980971',
'1235981010',
'1235981014',
'1235981033',
'1235981777',
'1235981321',
'1235981944',
'1235981200'
)
Some of the credit cards clients decided to cancel at last moment, but so those two records never got inserted into database. But I do still want to show those null value cards(since there's no data) from the list of cards(most are inserted). Is there way to do this without temp table?
Let's say I forgot which two card numbers are the ones that didn't got inserted into database, instead just show nothing or the 9 record, anyway to display those two null records as "real" data?
Assuming that you have a column with a card number stored in your database table cleansedcarddatabase called CardNumber, you could reformat your list of card numbers in to a list of values, and then join to your database table on the card number, then display all columns from your database table (because your OP is just using select * from the database table):
select
db_credit_cards.*
from
(values ('1235980878'),
('1235980918'),
('1235980969'),
('1235980971'),
('1235981010'),
('1235981014'),
('1235981033'),
('1235981777'),
('1235981321'),
('1235981944'),
('1235981200')) AS all_credit_cards(CardNumber)
left join cleansedcarddatabase as db_credit_cards ON all_credit_cards.CardNumber = db_credit_cards.CardNumber;
This would allow any records in cleansedcarddatabase to display the data that has been stored there, and then display nulls for the card numbers in your list of values that do not exist in the database table.
If you provide more information regarding the schema cleansedcarddatabase and your actual desired output columns, I can edit this ansewer with a more specific example using this technique.

fetching rows in big table with firedac

I have a big table on sql server, about 800k records.
How could I navigate through all the records, x amount at a time?
For example, I would like to open FDQuery, but browse 1000 records at a time.
If I use:
FDQuery.First;
while not FDQuery.eof do
begin
//do something
   FDQuery.Next;
end;
I believe all records are brought;
I have read about fetching records, properties like FetchOptions:
Mode, RowsetSize, RecsMax, RecsSkip ... but I can't browse all the records, a fixed number of records at a time.
You can use some creativity, provide pagination and fetch let's say 100 rows per page using a thread which is equipped with a nice progress bar in the UI. in this method you must manage search and filters by some smart queries, reloading data sometimes, etc...
IF you are using SQL server one of the options could be using some ranking functions like ROW_NUMBER to fetch each page.
Something like this:
;WITH CTE_Test AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY F1) AS Row_No FROM Tbl_Test
)
SELECT * FROM CTE_Test
WHERE Row_No BETWEEN 1 AND 100

Show records where most recent 'x' records meet criteria

Here's a simplified SQLFiddle example of data
Basically, I'm looking to identify records in a login audit table where the most recent records for each user has 'x' (let's say 3, for this example) number of failed logins
I am able to get this data for individual users by doing a SELECT TOP 3 and ordering by the log date in descending order and evaluating those records, but I know there's got to be a better way to do this.
I have tried a few queries using ROW_NUMBER(), partitioning by UserName and Success and ordering by LogDate, but I can't quite get it to do what I want. Essentially, every time a successful login occurs, I want the failed login counter to be reset.
try this code:
select * from (
select distinct a.UserName,
(select sum(cast(Success as int)) from (
SELECT TOP 3 Success --- here 3, change it to your number
FROM tbl as b
WHERE b.UserName=a.UserName
ORDER BY LogDate DESC
) as q
having count(*) >= 3 --- this string need to remove users who made less then 3 attempts
) as cnts
from tbl as a
) as q2
where q2.cnts=0
it shows users with all last 3 attempts failed, with different modifications, you can use this approach to identify how many success/fail attempts where done during last N rows
NOTE: this query works, but it is not the optimal way, from tbl as a should be changed to table where only users are stored, so you will be able to get rid of distinct, also - store users ID instead of username in tbl

Query optimisation in sybase

I need to retrieve customer MSISDN (phone no) from 22 customer databases. I have created a view for two cases:
First we need to check for which MSISDNs profile_id 16240 is inactive.This can be done by querying in database whose inactive data is not null.
Since for GPRS we have two profile 25054 and 16240,it happens e for MSISDNs 25054 (for internet purpose) is active and 16240 (for GPRS is not active)
so we need to create script for that purpose .
I have prepared a query:
CREATE VIEW SUBSCR_INFO_VIEW AS
SELECT subscr_no,account_no FROM CUSTOMER_PROFILE_DEF WHERE subscr_no NOT IN
(SELECT DISTINCT(subscr_no) FROM CUSTOMER_ID_EQUIP_MAP
WHERE inactive_date Is NOT NULL)
AND (profile_id IN (16240) AND cutoff_end_dt IS NOT NULL) OR (profile_id IN (25054) AND profile_id NOT IN (16240) AND cutoff_end_dt IS NULL)
SET ROWCOUNT 100
SELECT DISTINCT(subscr_no) FROM SUBSCR_INFO_VIEW
This will be hit in all 22 customer servers and to take data from a single customer it's taking 2.5 min. I want to reduce that time. Please let me know your feedback.
This is a little difficult to answer without knowing more about the structure of the database. How many records do you have in the CUSTOMER_PROFILE_DEF and CUSTOMER_ID_EQUIP_MAP tables, and what keys do you have? Also, your SQL is very difficult to understand in the original post, I have reformatted it below and made some small changes:
CREATE VIEW
SUBSCR_INFO_VIEW
AS SELECT
subscr_no,
account_no
FROM
CUSTOMER_PROFILE_DEF
WHERE
subscr_no
NOT IN (
SELECT DISTINCT
subscr_no
FROM
CUSTOMER_ID_EQUIP_MAP
WHERE
inactive_date Is NOT NULL
)
AND ((profile_id = 16240 AND cutoff_end_dt IS NOT NULL)
OR (profile_id = 25054 AND cutoff_end_dt IS NULL))
SET ROWCOUNT 100 -- This is just for testing?
SELECT DISTINCT(subscr_no) FROM SUBSCR_INFO_VIEW
The sql is largely the same, but I changed your profile_id in (12345) statements to profile_id = 12345 as there was only one value in the list of values.

User-sortable records

For each user in my webapp, there are n related Widgets. Each widget is represented in the database in a Widgets table. Users can sort their widgets, they'll never have more than a couple dozen widgets, and they will frequently sort widgets.
I haven't dealt with database items that have an inherent order to them very frequently. What's a good strategy for ordering them? At first, I thought a simple "sortIndex" column would work just fine, but then I started wondering how to initialize this value. It presumably has to be a unique value, and it should be greater or less than every other sort index. I don't want to have to check all of the other sort indexes for that user every time I create a new widget, though. That seems unnecessary.
Perhaps I could have a default "bottom-priority" sort index? But then how do I differentiate between those? I suppose I could use a creation date flag, but then what if a user wants to insert a widget in the middle of all of those bottom-priority widgets?
What's the standard way to handle this sort of thing?
If you have users sorting widgets for their own personal tastes, you want to create a lookup table, like so:
create table widgets_sorting
(
SortID int primary key,
UserID int,
WidgetID int,
SortIndex int
)
Then, to sort a user's widgets:
select
w.*
from
widgets w
inner join widgets_sorting s on
w.WidgetID = s.WidgetID
inner join users u on
s.UserID = u.UserID
order by
s.SortIndex asc
This way, all you'll have to do for new users is add new rows to the widgets_sorting table. Make sure you put a foreign key constraint and an index on both the WidgetID and the UserID columns.
These lookup tables are really the best way to solve the many-to-many relationships that are common with this sort of personalized listing. Hopefully this points you in the right direction!
The best way for user-editable sorting is to keep the id's in a linked list:
user_id widget_id prev_widget_id
---- ---- ----
1 1 0
1 2 8
1 3 7
1 7 1
1 8 3
2 3 0
2 2 3
This will make 5 widgets for user 1 in this order: 1, 7, 3, 8, 2; and 2 widgets for user 2 in this order: 3, 2
You should make UNIQUE indexes on (user_id, widget_id) and (user_id, prev_widget_id).
To get widgets in intended order, you can query like this, say, in Oracle:
SELECT w.*
FROM (
SELECT widget_id, level AS widget_order
FROM widget_orders
START WITH
user_id = :myuser
AND prev_widget_id = 0
CONNECT BY
user_id = PRIOR user_id
AND prev_widget_id = PRIOR widget_id
) o
JOIN widgets w
ON w.widget_id = o.widget_id
ORDER BY
widget_order
To update the order, you will need to update at most 3 rows (even if you move the whole block of widgets).
SQL Server and PostgreSQL 8.4 implement this functionality using recursive CTEs:
WITH
-- RECURSIVE
-- uncomment the previous line in PostgreSQL
q AS
(
SELECT widget_id, prev_widget_id, 1 AS widget_order
FROM widget_orders
WHERE user_id = #user_id
UNION ALL
SELECT wo.widget_id, wo.prev_widget_id, q.widget_order + 1
FROM q
JOIN wo.widget_orders wo
ON wo.user_id = #user_id
AND wo.prev_widget_id = q.widget_id
)
SELECT w.*
FROM q
JOIN widgets w
ON w.widget_id = q.widget_id
ORDER BY
widget_order
See this article in my blog on how to implement this functionality in MySQL:
Sorting lists
I like to use a two-table approach - which can be a bit confusing but if you're using an ORM such as ActiveRecord it's easy, and if you write a bit of clever code it can be manageable.
Use one table to link user to sorting, and one table to link widget and position and sorting. This way it's a lot clearer what's going on, and you can use an SQL join or a seperate query to pull the various data from the various tables. Your structure should look like this:
//Standard user + widgets table, make sure they both have unique IDs
CREATE TABLE users;
CREATE TABLE widgets;
//The sorting tables
CREATE TABLE sortings (
id INT, //autoincrement etc,
user_id INT
)
CREATE TABLE sorting_positions (
sorting_id INT,
widget_id INT,
position INT
)
Hopefully this makes sense, if you're still confused, comment on this message and I'll write you up some basic code.
Jamie
If you mean that each user assigns his own sort order to the widgets, then Eric's answer is correct. Presumably you then have to give the user a way to assign the sort value. But if the number is modest as you say, then you can just give him a screen listing all the widgets, and either let him type in the order number, or display them in order and put up and down buttons beside each, of if you want to be fancy, give him a way to drag and drop.
If the order is the same for all users, the question becomes, Where does this order come from? If it's arbitrary, just assign a sequence number as new widgets are created.

Resources