SQL retrieval from tables - sql

I have a table something like
EMPLOYEE_ID DTL_ID COLUMN_A COLUMN_B
---------------------------
JOHN 0 1 1
JOHN 1 3 1
LINN 0 1 12
SMITH 0 9 1
SMITH 1 11 12
It means for each person there will be one or more records with different DTL_ID's value (0, 1, 2 .. etc).
Now I'd like to create a T-SQL statement to retrieve the records with EMPLOYEE_ID and DTL_ID.
If the specified DTL_ID is NOT found, the record with DTL_ID=0 will be returned.
I know that I can achieve this in various ways such as checking if a row exists via EXISTS or COUNT(*) first and then retrieve the row.
However, I'd like to know other possible ways because this retrieval statement is very common in my application and my table have hundred thousand of rows.
In the above approach, I've had to retrieve twice even if the record with the DTL_ID specified exists, and I want to avoid this.

Like this:
SELECT *
FROM table
WHERE EMPLOYEE_ID = ?? AND DTL_ID = ??
UNION
SELECT *
FROM table
WHERE EMPLOYEE_ID = ?? AND DTL_ID = 0
AND NOT EXISTS (SELECT *
FROM table
WHERE EMPLOYEE_ID = ?? AND DTL_ID = ??)
You will of course have to fill in the ?? with the proper number.

If DTL_ID is always 0 or positive:
SELECT TOP 1 * FROM table
where EmployeeID = #EmployeeID and DTL_ID in (#DTL_ID,0)
order by DTL_ID desc
If you're working across multiple employees in a single query, etc, then you might want to use ROW_NUMBER() if your version of SQL supports it.

Use ISNULL(DTL_ID, 0) in your final SELECT query

SELECT E1.EMPLOYEE_ID, ISNULL(E2.DTL_ID, 0), E1.COLUMN_A, E1.COLUMN_B EMPLIYEES AS E1
LEFT JOIN EMPLIYEES AS E2
ON E1.EMPLOYEE_ID = E2.EMPLOYEE_ID AND E2.DTL_ID = 42

You can use top and union, e.g.:
declare #t table(id int, value int, c char)
insert #t values (1,0,'a'), (1,1,'b'), (1,2,'c')
declare #id int = 1;
declare #value int = 2;
select top(1) *
from
(
select *
from #t t
where t.value = #value and t.id = #id
union all
select *
from #t t
where t.value = 0
)a
order by a.value desc
If #value = 2 than query returns 1 2 c. If #value = 3 than query returns 1 0 a.

SELECT MAX(DTL_ID) ...
WHERE DTL_ID IN (#DTL_ID, 0)

Related

Extracting rows from fact table which have missing data, using metadata table

I have a situation where I have:
A fact table with an id column which is NOT unique but is never null. This fact also has a lot of other dimensions (columns) which may be with a default value -1 (which logically means null)
Example:
id | Dimension1 | Dimension2 | Dimension3
1 Value -1 Value
1 -1 -1 Value
2 -1 Value Value
A metadata table that has the same dimensions as the fact table. Each row in this table represents an unique id from the fact table. Rest of the columns are populated with either null or 1, where 1 means that this dimension is a required dimension in the fact table for this id.
Example:
id | Dimension1 | Dimension2 | Dimension3
1 1 1
2 1 1
My goal is to get ONLY the rows from the fact table that are missing required information according to the metadata table. So from the examples above I would get only the row with id = 1 where Dimension1 = -1, since metadata table says for id = 1 dimensions 1 and 3 are required.
Is there an easy way of doing this?
I have made a very complicated query where there is join between these two tables and a case checks between all dimensions (I have more than 100 of them). Then these checks assign a -1 if dimension is missing in fact but is required, and there is an outer query that would sum these for all rows and only pick up rows with negative sum.
It does not work to 100% and I think its way too complicated to run on a real big fact table, so I'm open to ideas.
edit: Dynamic SQL is not allowed :(
I would suggest using a cte and an except query... in this solution, you will have to add the cases as well, but the join seems far more simple to me and you don't need to sum up any dummy values...
DECLARE #t TABLE(
id int, Dimension1 int, Dimension2 int, Dimension3 int
)
DECLARE #tMeta TABLE(
id int, Dimension1 int, Dimension2 int, Dimension3 int
)
INSERT INTO #t VALUES (1, 123, -1, 345), (1, -1, -1, 246), (2, -1, 567, 987)
INSERT INTO #tMeta VALUES (1, 1, NULL, 1), (2, NULL, 1, 1)
;WITH cte AS(
SELECT id,
CASE WHEN Dimension1 = -1 THEN NULL ELSE 1 END Dimension1,
CASE WHEN Dimension2 = -1 THEN NULL ELSE 1 END Dimension2,
CASE WHEN Dimension3 = -1 THEN NULL ELSE 1 END Dimension3
FROM #t
EXCEPT
SELECT *
FROM #tMeta
EXCEPT
SELECT id, ISNULL(Dimension1,1), ISNULL(Dimension2,1), ISNULL(Dimension3,1)
FROM #tMeta
)
SELECT t.*
FROM #t t
JOIN cte c ON t.id = c.id
AND CASE WHEN t.Dimension1 = -1 THEN -1 ELSE 1 END = ISNULL(c.Dimension1, -1)
AND CASE WHEN t.Dimension2 = -1 THEN -1 ELSE 1 END = ISNULL(c.Dimension2, -1)
AND CASE WHEN t.Dimension3 = -1 THEN -1 ELSE 1 END = ISNULL(c.Dimension3, -1)
You can use UNPIVOT to simplify query also you don't have ROWId in your fact table so the first CTE to make ROW_NUMBER() works as a RowId in the fact table. Then we make unpivoted tables (fact and template table) and join them:
WITH TFBase AS
(
SELECT TF.*, ROW_NUMBER() OVER (ORDER BY ID) as TableRowID FROM TF
),
TFU AS
(
select id,TableRowID,dim,val
from TFBase
unpivot
(
val for dim in (Dimension1, Dimension2, Dimension3)
) u
WHERE U.Val <>-1
)
,
TFT AS
(
select id,dim,val
from TTemplate
unpivot
(
val for dim in (Dimension1, Dimension2, Dimension3)
) u
WHERE Val is NOT NULL
)
SELECT * FROM TFBase WHERE
TableRowID IN
(
SELECT TableRowID FROM TFU
LEFT JOIN TFT ON
(TFU.id=TFT.id) AND (TFU.dim = TFT.dim)
GROUP BY TableRowID, TFU.ID
HAVING COUNT(TFT.Val) <> (SELECT COUNT(*) FROM TFT WHERE ID = TFU.ID)
)

Determine if any values satisfy a condition

How do I write an effiecient query to determine if 1 or more values are 10+ in a column. I am aware that I can count the values but this will scan all the records. Here is what I have so far:
SELECT COUNT(*) FROM MyTable WHERE [state] = 12 AND age > 110
I want this query to stop when it find the first person over 110 not scan the entire table. Is this possible?
You can use a subquery to return 1 or no row using this query:
SELECT TOP 1 1 as row_exists
FROM MyTable
WHERE [state] = 12 AND age > 110;
You can use a subquery to return 1 or NULL using this as a subquery:
SELECT (SELECT TOP 1 1 FROM MyTable WHERE [state] = 12 AND age > 110
) as row_exists;
You can put this into T-SQL using:
IF (EXISTS (SELECT 1 FROM MyTable WHERE [state] = 12 AND age > 110))
BEGIN
. . .
END;
TOP is not needed in an EXISTS subquery.
So you wish to have the scalar Boolean result? The exists will quite once any row matches the condition
DECLARE #Result bit =
(SELECT CASE WHEN EXISTS(SELECT * FROM MyTable WHERE [state] = 12 AND age > 110) THEN 1 ELSE 0 END)
I am not sure wether the it is helpful for you, but you can try to test:
For example you can want to determine the result set row count is 100.
you can use top 100 base your statement.
if the orignal result lines is more than 100, then ##ROWCOUNT will be true.
SELECT TOP 100 FROM MyTable WHERE [state] = 12 AND age > 110
IF ##ROWCOUNT=100
PRINT 'True'
ELSE
PRINT 'Flase'

Select statement to return constant when no records found in table in SQL Server 2008

I am have a table with data and now i need to return zero in select statement if there is no records in table for example. I need to use it in Stored Procedure.
-- If no records exists in below select statement
SELECT ID,Text,Date FROM tblData WHERE ID = 12
IF (##ROWCOUNT = 0)
BEGIN
SELECT -5 AS ID
END
Output:
ID Text Date
ID
-5
Expected output
ID
-5
If you want to return 1 row even when there is no match, you can use aggregation:
SELECT (CASE WHEN COUNT(*) = 0 THEN -5 ELSE MAX(ID) END) as ID
FROM tblData
WHERE ID = 12;
I always use an Exists statment.
if exists(SELECT ID FROM tblData WHERE ID = 12)
select 0 as RowsExist
else
select 1 as RowsExist
For a single scalar value you could use something like;
SELECT ISNULL((SELECT ID FROM tblData WHERE ID = 12), 0) as ID
Rhys
SELECT (CASE WHEN Ta.ID IS NULL THEN TBL.ID
ELSE Ta.ID END) AS ID,Ta.Text,Ta.Date
FROM (VALUES(-5)) AS TBL(ID)
LEFT JOIN
(
SELECT ID,Text,Date FROM tblData WHERE ID = 12
)
AS Ta ON Ta.ID = Ta.ID

SQL Specific Item on top

I am having this query for a photo contest:
SELECT * FROM `users` ORDER BY entry_id DESC
The result gives 10 records with entry_id 10, 9, 8, 7, ......1
What can I do to pick a specific entry on the top?
As there is a requirement if there is a refer ID, entry show first.
So the expected result should be: 4,10,9,8,7,6,5,3,2,1 if 4 if a refer ID.
Try this:
SELECT *
FROM `users`
ORDER BY (CASE WHEN entry_id = 4 THEN 0 ELSE 1 END), entry_id DESC;
for more Dynamic Approach create table value Function
create function OrderData(#a int)
returns #t table ( id int)
as
begin
insert into #t
SELECT *
FROM ab
ORDER BY (CASE WHEN id = #a THEN 0 ELSE 1 END), id DESC
return;
end;
select * from dbo.abc(4)
output
4,10,9,8,7,6,5,3,2,1
select * from dbo.abc(5)
output
5,10,9,8,7,6,4,3,2,1

Check that all rows match a given criterion

requestId Consultantid statusid
1 2 10
2 2 10
3 2 10
I want to check if every row has a statusid of 10.
if (every row has a statusid of 10) then
-----do this
endif;
I'm a bit rusty on PL-SQL, but something like this would work in T-SQL:
if not exists (select * from your_table where statusid <> 10) then
-- whatever
end
Edit:
Ok, apparantly in PL-SQL, you need to do something like this:
DECLARE
notallten INTEGER;
BEGIN
SELECT COUNT(*) INTO notallten
FROM your_table
WHERE statusid <> 10
AND ROWNUM = 1;
IF notallten = 0 THEN
-- Do something
END IF;
END;
I don't have an Oracle server to test on though.
declare
v_exists_status_10 number(1);
...
begin
...
-- v_exists_status_10 = 0 if no such row exist, 1 if at least one does
select count(*)
into v_exists_status_10
from dual
where exists
(select * from your_table where statusid <> 10);
if v_exists_status_10 > 0 then
...
Note that you could also do a dumb COUNT() into a variable, but it could be massively inefficient compared to EXISTS. With a COUNT() you'd have to scan all the records, whereas with an EXISTS as soon as it hits a statusid = 10 it can stop scanning.
Simpler solution, that accounts for NULLs in statusid:
for r in (
select 1 dummy
from your_table
where (statusid != 10 or statusid is null)
and rownum = 1) loop
-----do this
end loop;