How do I retrieve non-empty & non-duplicate data from the database? - c#

I have this nitpicked columns on my table (cause the rest are irrelevant in the problem).
ID | Generic Name
-----+---------------
001 | Cetirizine
002 | Cetirizine
003 |
004 | Paracetamol
I want my combo box to display only a single entry Cetirizine (or any data that has been duplicated) and no empty generic names (some data have no generic names).
I've tried:
select
Item_GenName
from
ItemMasterlistTable
where
nullif(convert(varchar, Item_GenName), '') is not null
but it only achieves the no empty data part.
I've tried using DISTINCT, but it doesn't work and somebody suggested JOIN but I don't think it works since I'm only using 1 table.
I've also tried:
SELECT
MIN(Item_ID) AS Item_ID, Item_GenName
FROM
ItemMasterlistTable
GROUP BY
Item_GenName
but there's always an error:
The text, ntext, and image data types cannot be compared or sorted, except when using IS NULL or LIKE operator.

The following query should return only distinct, non-empty Item_GenNames:
SELECT DISTINCT Item_GenName
FROM ItemMasterlistTable
// because Item_GenName is of type *text*, the below in lieu of `is not null` and `!= ''`
WHERE datalength(Item_GenName) != 0
You said you tried DISTINCT and it did not work so I want to clarify,
The DISTINCT keyword will return unique records over the complete domain of your select statement. If you include the ID column in your select statement, even a distinct selection will return your duplicate Item_GenNames b/c the combined ID / Item_GenName record would be unique. Include only Item_GenName in your select clause to guarantee distinct values for this column.

The following query might be useful.
declare #tab table (ID varchar(10), Generic_Name varchar(100))
insert into #tab
select '001', 'Cetirizine'
union
select '002', 'Cetirizine'
union
select '003', ''
union
select '004', 'Paracetamol'
select MIN(substring(ID, 1, 10)) ID, substring(Generic_Name, 1, 1000) Generic_Name
from #tab
where substring(Generic_Name, 1, 1) <> ''
group by substring(Generic_Name, 1, 1000)

You can try this query
Select distinct Item_GenName FROM(
Select * FROM ItemMasterlistTable where Item_GenName <> ''
)t
Inner query remove non-empty records and outer query get the distinct record from the inner output

Related

Use case with a variable to determine whether a count has to be returned or column list

I have a situation where on a dashboard, for pending approvals I am trying to show certain items as follows
Item 1 [Count]
Item 2 [Count]
Item 3 [Count]
The [Count] shows a numeric value of items pending approval. On click of each of these items, there is an associated table where the records are being shown.
The way of deriving these counts is very complex and I wish to avoid making duplicate queries for count for example query #1 as
SELECT COUNT(*)
FROM tableName
and then query #2 as
SELECT ColumnA, ColumnB, ColumnC
FROM tableName
Since these queries are being read into my C# application, until now I've been doing the following
var onlyCount = true;
var subQuery = onlyCount? "COUNT(*)": "ColumnA, ColumnB, ColumnC";
var query = $"SELECT {subQuery} FROM tableName";
But with an ever-growing list of columns that needs to be managed, this makes the code look ugly. With calculated data in the select list, Case(s), IIF(s) in the statement the above-said solution is no longer a "maintainable" solution. With the select query is something as demonstrated below even possible?
DECLARE #CountOnly AS BIT = 1
SELECT
CASE
WHEN #CountOnly = 1
THEN COUNT(*)
ELSE ColumnA, ColumnB, ColumnC
END
FROM
tableName
Have any one ever faced such a scenario? Or if you could point me in a direction where this can be handled better?
Side note: The above query is being passed into a SqlDataReader to fetch the data and show to the end user.
You may want to use something like this:
DECLARE #CountOnly AS BIT = 1
IF (#CountOnly = 1)
BEGIN
SELECT ColumnA, ColumnB, ColumnC
FROM MyTable
ELSE
SELECT COUNT(*)
FROM MyTable
END

Strange order of line insertion

I have a stored procedure that inserts a line in a table. This table has an auto incremented int primary key and a datetime2 column named CreationDate. I am calling it in a for loop via my C# code, and the loop is inside a transaction scope.
I run the program twice, first time with a for loop that turned 6 times and second time with a for loop that turned 2 times. When I executed this select on sql server I got a strange result
SELECT TOP 8
RequestId, CreationDate
FROM
PickupRequest
ORDER BY
CreationDate DESC
What I didn't get is the order of insertion: for example the line with Id=58001 has to be inserted after that with Id=58002 but this is not the case. Is that because I put my loop in a transaction scoope? or the precision in the datetime2 is not enough?
It is a question of speed and statement scope as well...
Try this:
--This will create a #numbers table with 1 mio numbers:
DECLARE #numbers TABLE(Nbr BIGINT);
WITH N(N) AS
(SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1)
,MoreN(N) AS
(SELECT 1 FROM N AS N1 CROSS JOIN N AS N2 CROSS JOIN N AS N3 CROSS JOIN N AS N4 CROSS JOIN N AS N5 CROSS JOIN N AS N6)
INSERT INTO #numbers(Nbr)
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM MoreN;
--This is a dummy table for inserts:
CREATE TABLE Dummy(ID INT IDENTITY,CreationDate DATETIME);
--Play around with the value for #Count. You can insert 1 mio rows in one go. Although this runs a while, all will have the same datetime value:
--Use a small number here and below, still the same time value
--Use a big count here and a small below will show a slightly later value for the second insert
DECLARE #Count INT = 1000;
INSERT INTO Dummy (CreationDate)
SELECT GETDATE()
FROM (SELECT TOP(#Count) 1 FROM #numbers) AS X(Y);
--A second insert
SET #Count = 10;
INSERT INTO Dummy (CreationDate)
SELECT GETDATE()
FROM (SELECT TOP(#Count) 1 FROM #numbers) AS X(Y);
SELECT * FROM Dummy;
--Clean up
GO
DROP TABLE Dummy;
You did your insertions pretty fast so the actual CreationDate values inserted in one program run had the same values. In case you're using datetime type, all the insertions may well occur in one millisecond. So ORDER BY CreationDate DESC by itself does not guarantee the select order to be that of insertion.
To get the desired order you need to sort by the RequestId as well:
SELECT TOP 8 RequestId, CreationDate
FROM PickupRequest
ORDER BY CreationDate DESC, RequestId DESC

While Selecting based on ROWID - Distinct or Group By doesn't work in oracle

I have query like below , I tried to filter out duplicate columns by using Group BY
SELECT contacts.rowid AS ROW_PASS,
duty_rota.rowid AS ROW_PASS_ROTA,
duty_rota.duty_type AS DUTY_TYPE
FROM duty_rota,
duty_types,
contacts
WHERE duty_rota.duty_type = duty_types.duty_type
AND duty_rota.duty_officer = contacts.duty_id
AND sname IS NOT NULL
GROUP BY contacts.rowid,
duty_rota.rowid,
duty_rota.duty_type
ORDER BY duty_date
After playing with the query little bit I came to know we can't filter out distinct using group by while using ROWID. So can somebody please help me to write code (in SQL) with a logic that
if (any row is completely identical with another row of the query o/p)
{
then display only one column
}
I will be using the output as gridview's data source in C#, so if not in SQL - can you help me whether somehow in C# I can achieve to display only identical columns?
If you want to filter duplicate rows, you can use this query:
SELECT Max(duty_rota.rowid) AS ROW_PASS_ROTA,
duty_rota.duty_type AS DUTY_TYPE
FROM duty_rota,
duty_types,
contacts
WHERE duty_rota.duty_type = duty_types.duty_type
AND duty_rota.duty_officer = contacts.duty_id
AND sname IS NOT NULL
GROUP BY duty_rota.duty_type
ORDER BY DUTY_TYPE
Here you go: http://sqlfiddle.com/#!2/2a038/2
Take out the ROWID's. Example: If your table has 3 columns (colA, colB, colC) you could find exact row dups this way...
select a.* from
(
select count(*) dupCnt, colA, colB, colC from myTable
group by colA, colB, colC
) a
where dupCnt > 1
First, the ROWID is a unique field for each row, so using this field you will never have duplicates. The only solution here is to not use it. It's data does not hold anything you would want to display anyway.
Simply put, if you want no duplicates, you need the DISTINCT keyword:
SELECT DISTINCT field1,
field2
FROM table1,
table2
WHERE table1.key1 = table2.key1;
This will select all Field1, Field2 combinations from the two tables. Due to the DISTINCT keyword, each line will only be in the result list once. Duplicates will not be in the result list.
SELECT DISTINCT duty_rota.duty_type AS DUTY_TYPE
FROM duty_rota,
duty_types,
contacts
WHERE duty_rota.duty_type = duty_types.duty_type
AND duty_rota.duty_officer = contacts.duty_id
AND sname IS NOT NULL
ORDER BY duty_date
You will only need to GROUP BY if you need further operations on the result set, like counting the duplicates. If all you need is "no duplicates", the DISTINCT keyword is exactly what you are looking for.
Edit:
In case I misread your question and you want to see only those, that are duplicates, you need to group and you need to filter based on the groups criteria. You can do that using the HAVING clause. It's kind of an additional WHERE of the groups criteria:
SELECT FIELD1, FIELD2, COUNT(*)
FROM TABLE1, TABLE2
WHERE TABLE1.KEY1 = TABLE2.KEY1
GROUPB BY FIELD1, FIELD2
HAVING COUNT(*) > 1

How to set an integer value to one if a record exist in database C# Sql Query

getName_as_Rows is an array which contains some names.
I want to set an int value to 1 if record found in data base.
for(int i = 0; i<100; i++)
{
using (var command = new SqlCommand("select some column from some table where column = #Value", con1))
{
command.Parameters.AddWithValue("#Value", getName_as_Rows[i]);
con1.Open();
command.ExecuteNonQuery();
}
}
I am looking for:
bool recordexist;
if the above record exist then bool = 1 else 0 with in the loop.
If have to do some other stuff if the record exist.
To avoid making N queries to the database, something that could be very expensive in terms of processing, network and so worth, I suggest you to Join only once using a trick I learned. First you need a function in your database that splits a string into a table.
CREATE FUNCTION [DelimitedSplit8K]
--===== Define I/O parameters
(#pString VARCHAR(8000), #pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000...
-- enough to cover VARCHAR(8000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "zero base" and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT 0 UNION ALL
SELECT TOP (DATALENGTH(ISNULL(#pString,1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT t.N+1
FROM cteTally t
WHERE (SUBSTRING(#pString,t.N,1) = #pDelimiter OR t.N = 0)
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY s.N1),
Item = SUBSTRING(#pString,s.N1,ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1,8000))
FROM cteStart s
GO
Second, concatenate your 100 variables into 1 string:
"Value1", "Value 2", "Value 3"....
In Sql Server you can just join the values with your table
SELECT somecolumn FROM sometable t
INNER JOIN [DelimitedSplit8K](#DelimitedString, ',') v ON v.Item = t.somecolumn
So you find 100 strings at a time with only 1 query.
Use var result = command.ExecuteScalar() and check if result != null
But a better option than to loop would be to say use a select statement like
SELECT COUNT(*) FROM TABLE WHERE COLUMNVAL >= 0 AND COLUMNVAL < 100,
and run ExecuteScalar on that, and if the value is > 0, then set your variable to 1.

special select query

I have 3 tables in my sql database like these :
Documents : (DocID, FileName) //list of all docs that were attached to items
Items : (ItemID, ...) //list of all items
DocumentRelation : (DocID, ItemID) //the relation between docs and items
In my winform application I have showed all records of Items table in a grid view and let user to select several rows of it and then if he press EditAll button another grid view should fill by file name of documents that are related to these selected items but not all of them,
Just each of documents which have relation with ALL selected items
Is there any query (sql or linq) to select these documents?
Try something like:
string query;
foreach (Item in SelectedItems)
{
query += "select DocID from DocumentRelation where ItemID =" + Item.Id;
query += "INTERSECT";
}
query -= "INTERSECT";
And exec the Query;
Take one string and keep on adding itemid comma separated in that,like 1,2,3 and then write query like
declare ItemID varchar(50);
set ItemID='1,2,3';
select FileName
from documents
Left Join DocumentRelation on Documents.DocId = DocumentRelation.DocId
where
DocumentRelation.ItemID in (select * from > dbo.SplitString(ItemID))
and then make one function in database like below
ALTER FUNCTION [dbo].[SplitString] (#OrderList varchar(1000))
RETURNS #ParsedList table (OrderID varchar(1000) )
AS BEGIN
IF #OrderList = ''
BEGIN
set #OrderList='Null'
end
DECLARE #OrderID varchar(1000), #Pos int
SET #OrderList = LTRIM(RTRIM(#OrderList))+ ','
SET #Pos = CHARINDEX(',', #OrderList, 1)
IF REPLACE(#OrderList, ',', '') <''
BEGIN
WHILE #Pos 0
BEGIN
SET #OrderID = LTRIM(RTRIM(LEFT(#OrderList, #Pos - 1)))
IF #OrderID < ''
BEGIN
INSERT INTO #ParsedList (OrderID)
VALUES (CAST(#OrderID AS varchar(1000)))
--Use Appropriate conversion
END
SET #OrderList = RIGHT(#OrderList, LEN(#OrderList) - #Pos)
SET #Pos = CHARINDEX(',', #OrderList, 1)
END
END
RETURN
END
Linq
var td =
from s in Items
join r in DocumentRelation on s.ItemID equals r.ItemID
join k in Documents on k.DocID equals r.DocID
where Coll.Contains (s.ItemID) //Here Coll is the collection of ItemID which you can store when the users click on the grid view row
select new
{
FileName=k.FileName,
DocumentID= k.DocId
};
You can loop through td collection and bind to your grid view
SQL
create a stored proc to get the relevant documents for the itemID selected from the grid view and paramterize your in clause
select k.FileName,k.DocId from Items as s inner join
DocumentRelation as r on
s.ItemID=r.ItemID and r.ItemId in (pass the above coll containing selected ItemIds as an input the SP)
inner join Documents as k
on k.DocId=r.DocIk
You can get the information on how to parametrize your sql query
Here's one approach. I'll let you figure out how you want to supply the list of items as arguments. And I also assume that (DocID, ItemID) is a primary key in the relations table. The having condition is what enforces your requirement that all select items are related to the list of documents you're seeking.
;with ItemsSelected as (
select i.ItemID
from Items as i
where i.ItemID in (<list of selected ItemIDs>)
)
select dr.DocID
from DocumentRelation as dr
where dr.ItemID in (select ItemID from ItemsSelected)
group by dr.DocID
having count(dr.ItemID) = (select count(*) from ItemsSelected);
EDIT
As far as I can tell, the accepted answer is equivalent to the solution here despite OP's comment below.
I did some quick tests with a very long series of intersect queries and confirmed that you can indeed expect that approach to become gradually slower with an increasing number of selected items. But a much worse problem was the time taken just to compile the queries. I tried this on a very fast server and found that that step took about eight seconds when roughly one hundred intersects were concatenated.
SQL Fiddle didn't let me do anywhere near as many before producing this error (and taking more than ten seconds in the process): The query processor ran out of internal resources and could not produce a query plan. This is a rare event and only expected for extremely complex queries or queries that reference a very large number of tables or partitions. Please simplify the query. If you believe you have received this message in error, contact Customer Support Services for more information.
There are several possible methods of passing a list of arguments to SQL Server. Assuming that you prefer the dynamic query solution I'd argue that this version is still better while also noting that there is a SQL Server limit on the number of values inside the in.
There are plenty of ways to have this stuff blow up.

Categories

Resources