Need SQL assistance (Exclude, Exists, isNumeric) - sql

I am working on the the report which get my nerves because I cannot do simple query to exclude users which does not exists in order table. This supposed to be easy query but the I need to join two tables.
Select User_ID, FirstName, Surname, UserType
FROM user
Left Join Order. ID ON Order.User_Id = User.User_ID
User_ID = Order_Ref WHERE Oder_Ref IS NULL
Where UserType ='Super'
Now I come across report where Order.User_ID is has varchar data type and unfortunately, I cannot create query that extract data properly. Properly because I tried use isNumeric(Order.User_Id), use regular expressions, WHERE Order.User_Id not like '%[^0-9]%', and Order.User_Id != '', use not exists statements. None of the above helped really.
At the moment I have end up with:
Select User.User_Id, isNumeric(Order.Order_Id)
FROM User Left Join
Order
ON User.User_Id = Order.User_Id
WHERE (Order.Order_Id IS NULL AND ISNUMERIC(Order.Order_Id) = 1 AND User.UserType = 'Super')
This query is compiled without any problems but it does not return anything.
Could you please give me a hint what would be approach to do it.
I really appreciate for your help.

First - if user_id is the same thing in your [user] and [order] tables then they should have the same data type, referential integrity should be enforced (e.g. via PK/FK relationship). I understand that this may not be your call however.
Next - ISNUMERIC does not do what people think it does. To understand what I'm saying run these queries and try to figure out what ISNUMERIC really does:
select ISNUMERIC('$'), ISNUMERIC($), ISNUMERIC('$.,'), ISNUMERIC('$,'), ISNUMERIC('10,,0'), ISNUMERIC('');
select ISNUMERIC(''), cast('' as int), ''+2;
--select ISNUMERIC('$'), '$'+2; -- this errors, but is still worth runnning
For what you are describing I would use a subquery and do your filtering before the join like this:
Note that I included a couple ways your could check for non-numeric characters.
select [User].User_Id, isNumeric([Order].Order_Id)
from [User]
Left Join
(
select [order].Order_Id
from [order]
where [order].Order_Id NOT like '%[^0-9]%' -- contains only numbers
--where patindex('%[^0-9]%', [order].Order_Id) = 0
--where try_cast([order].Order_Id as int) is not null -- SQL Server 2012+
and [User].UserType = 'Super'
) [order]
on [User].User_Id = [order].User_Id

Change
Left Join Order ON Order.User_Id = User.User_ID
to
inner Join Order ON Order.User_Id = User.User_ID
That will restrict the results to users with orders. Your posted code has syntax errors you have to fix.

Related

T-SQL : check if data exists in table

I am using a view that checks if data exists in another table and if it exists if shows the number of row's connected to that table. Now I was wondering if there might be a faster way of doing this, since I am only interested if there is data and not necessary how many data rows there are. I guess when it does not need to count it will be faster.
This is what I use now:
SELECT
dbo.user.id,
dbo.user.userCode,
COALESCE (TotalProducts.ProductsInback, 0) AS ProductsInback
FROM
dbo.user
LEFT OUTER JOIN
(SELECT COUNT(id_product) AS ProductsInback, userCode
FROM dbo.Product
GROUP BY userCode) AS TotalProducts ON dbo.Product.userCode = TotalProducts.userCode
WHERE
dbo.user.userCode = 'XYZ'
Now this works all fine an it gives me the number of products connected to the user XYZ that are in the back of the store. However I just want to know if the user has products in the back of the store, I don't need to know how many. That seems to me a faster solution (walking anyway to the back of the store). So replacing the COUNT with ... ?
You are right, for a lookup whether data exists in another table, we use EXISTS or IN, because we don't have to go through all matching rows then, but can stop at the first one found.
EXISTS:
SELECT
id,
userCode,
CASE WHEN EXISTS (SELECT * FROM dbo.Product p WHERE p.userCode = u.userCode )
THEN 1 ELSE 0 END AS ProductsInback
FROM dbo.user u
WHERE u.userCode = 'XYZ'
IN:
SELECT
id,
userCode,
CASE WHEN userCode IN (SELECT userCode FROM dbo.Product)
THEN 1 ELSE 0 END AS ProductsInback
FROM dbo.user
WHERE userCode = 'XYZ'
If you change your left join to an inner join, you will just get a list of users with products. The other users will not appear on the list.
SELECT
dbo.user.id,
dbo.user.userCode
FROM dbo.user
JOIN dbo.Product
ON dbo.Product.userCode= TotalProducts.userCode

Aggregate query with subquery (SUM)

I have the following query:
SELECT UserId, (
0.099 *
(
CASE WHEN
(SELECT AcceleratedProfitPercentage FROM CustomGroups cg
INNER JOIN UserCustomGroups ucg ON ucg.CustomGroupId = cg.Id
WHERE Packs.UserCustomGroupId = ucg.Id)
IS NOT NULL THEN
((SELECT AcceleratedProfitPercentage FROM CustomGroups cg
INNER JOIN UserCustomGroups ucg ON ucg.CustomGroupId = cg.Id
WHERE Packs.UserCustomGroupId = ucg.Id)*1.0) / (100*1.0)
ELSE 1
END
)
)
As amount
FROM Packs WHERE Id IN (
SELECT ap.Id FROM Packs ap JOIN Users u ON ap.UserId = u.UserId
WHERE ap.MoneyToReturn > ap.MoneyReturned AND
u.Mass LIKE '1%');
which is producing correct output. However I have no idea how to aggregate it properly. I tried to use standard GROUP BY but I get the error (Column 'Packs.UserCustomGroupId' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY claus). Any ideas? Here is the output I currently get:
I want to aggregate it by UserId. Thanks in advance.
The option that involves the least query-rewriting is to drop your existing query into a CTE or temp table, like so:
; with CTE as (MyQueryHere)
Select UserID, sum(amount)
from CTE
Group by UserID
Wow that is one crazy query you've got going on there.
Try this:
SELECT UserId,
0.099 * SUM(t.Amount) AS [Amount SUM]
FROM Packs P
JOIN Users U
ON P.UserID = U.UserID
LEFT OUTER JOIN UserCustomGroups UCG
ON P.UserCustomGroupID = UCG.ID
LEFT OUTER JOIN CustomGroups CG
ON UCG.CustomGroupID = CG.ID
CROSS APPLY
(
SELECT CASE WHEN CG.ID IS NULL
THEN 1
ELSE CG.AcceleratedProfitPercentage / 100
END AS [Amount]
) t
WHERE P.MoneyToReturn > P.MoneyReturned
AND U.Mass LIKE '1%'
GROUP BY UserID
First, multiplying any number by 1 is pretty pointless, yet I see it twice in your original post. I'm not sure what led to that, but it's unnecessary.
Also, using CROSS APPLY will eliminate the need for you to repeat your subquery. Granted, it's slower (since it'll run on every row returned), but I think it makes sense in this case...Using left outer joins instead of CASE - SELECT - IS NULL makes your query much more efficient and much more readable.
Next, it appears that you are attempting to SUM percentages. Not sure what kind of data you're looking to return, but perhaps AVG would be more appropriate? I can't think of any practical reason why you would be looking to do that.
Lastly, APH's answer will most certainly work (assuming your original query works), but given the obfuscation and inefficiency of your query, I would definitely rewrite it.
Please let me know if you have any questions.

PostgreSQL query returning multiple rows instead of one

I have two tables: user and projects, with a one-to-many relationship between two.
projects table has field status with project statuses of the user.
status can be one of:
launched, confirm, staffed, overdue, complete, failed, ended
I want to categorize users in two categories:
users having projects in launched phase
users having projects other than launched status.
I am using the following query:
SELECT DISTINCT(u.*), CASE
WHEN p.status = 'LAUNCHED' THEN 1
ELSE 2
END as user_category
FROM users u
LEFT JOIN projects p ON p.user_id = u.id
WHERE (LOWER(u.username) like '%%%'
OR LOWER(u.personal_intro) like '%%%'
OR LOWER(u.location) like '%%%'
OR u.account_status != 'DELETED'
AND system_role=10 AND u.account_status ='ACTIVE')
ORDER BY set_order, u.page_hits DESC
LIMIT 10
OFFSET 0
I am facing duplicate records for following scenario:
If user has projects with status launched as well as overdue, complete or failed, then that user is recorded two times as both the conditions in CASE are satisfying for that user.
Please suggest a query where a user that has any project in launched status gets his user_category set to 1. The same user should not be repeated for user_category 2.
The query is probably not doing what you think it does for a number of reasons
There is DISTINCT and there is DISTINCTON(col1, col2).
DISTINCT (u.*) is no different from DISTINCT u.*. The parentheses are just noise.
AND binds before OR according to operator precedence. I suspect you want to use parentheses around the conditions OR'ed together? Or do you need it the way it is? But you don't need parentheses around the whole WHERE clause in any case.
Your expression LOWER(u.username) LIKE '%%%' doesn't make any sense. Every non-null string qualifies. Can be replaced with u.username IS NOT NULL. I suspect you want something different?
Postgres is case sensitive in string handling. You write of status being 'launched' etc. but use 'LAUNCHED' in your query. Which is it?
A couple of table qualifications are missing from the question making it ambiguous for the reader. I filled in as I saw fit.
Everything put together, it might work like this:
SELECT DISTINCT ON (u.set_order, u.page_hits, u.id)
u.*
, CASE WHEN p.status = 'LAUNCHED' THEN 1 ELSE 2 END AS user_category
FROM users u
LEFT JOIN projects p ON p.user_id = u.id
WHERE LOWER(u.username) LIKE '%%%' -- ???
OR LOWER(u.personal_intro) LIKE '%%%'
OR LOWER(u.location) LIKE '%%%'
OR u.account_status != 'DELETED' -- with original logic
AND u.system_role = 10
AND u.account_status = 'ACTIVE'
ORDER BY u.set_order, u.page_hits DESC, u.id, user_category
LIMIT 10
Detailed explanation in this related question:
Select first row in each GROUP BY group?
Two EXISTS semi-joins instead of the DISTINCT ON and CASE might be faster:
SELECT u.*
, CASE WHEN EXISTS (
SELECT FROM projects p
WHERE p.user_id = u.id AND p.status = 'LAUNCHED')
THEN 1 ELSE 2 END AS user_category
FROM users u
WHERE
( LOWER(u.username) LIKE '%%%' -- ???
OR LOWER(u.personal_intro) LIKE '%%%'
OR LOWER(u.location) LIKE '%%%'
OR u.account_status != 'DELETED' -- with alternative logic?
)
AND u.system_role = 10 -- assuming it comes from users ???
AND u.account_status = 'ACTIVE'
AND EXISTS (SELECT 1 FROM projects p WHERE p.user_id = u.id)
ORDER BY u.set_order, u.page_hits DESC
LIMIT 10;
You can use MIN() on your CASE result, and it seems dropping the DISTINCT would be a wise choice:
SELECT u.*, MIN(CASE
WHEN p.status = 'LAUNCHED' THEN 1
ELSE 2
END) as user_category
...
GROUP BY <list all columns in the users table>
...
Since "launched" gives a 1, using MIN() will not only force a single result but will also give preference to "launched" over the other states.

how can i eliminate duplicates in gridview?

i am retrieving data from three tables for my requirement so i wrote the following query
i was getting correct result but the problem is records are repeated whats the problem in
that query. i am binding result of query to grid view control. please help me
SELECT DISTINCT (tc.coursename), ur.username, uc. DATE, 'Paid' AS Status
FROM tblcourse tc, tblusereg ur, dbo.UserCourse uc
WHERE tc.courseid IN (SELECT ur1.courseid
FROM dbo.UserCourse ur1
WHERE ur1.userid = #userid)
AND ur.userid = #userid
AND uc. DATE IS NOT NULL
AND ur.course - id = uc.course - id
There is no JOIN between tblcourse tc,tblusereg ur. So you get a cross join despite the IN (which is actually a JOIN)
DISTINCT works on the whole row too: not one column.
Note: you mention dbo.UserCourse twice but use different column names courseid and [course-id]
Rewritten with JOINs.
select distinct
tc.coursename, ur.username, uc.[date], 'Paid' as [Status]
from
dbo.tblcourse tc
JOIN
dbo.tblusereg ur ON tc.courseid = ur.[course-id]
JOIN
dbo.UserCourse uc ON ur.[course-id] = uc.[course-id]
where
ur.userid=#userid
and
uc.[date] is not null
This may fix your problem...
Change that first part of your query
select distinct (tc.coursename),
TO
select distinct tc.coursename,
to make all the columns distinct not just tc.coursename

Help with Complicated SELECT query

I have this SELECT query:
SELECT Auctions.ID, Users.Balance, Users.FreeBids,
COUNT(CASE WHEN Bids.Burned=0 AND Auctions.Closed=0 THEN 1 END) AS 'ActiveBids',
COUNT(CASE WHEN Bids.Burned=1 AND Auctions.Closed=0 THEN 1 END) AS 'BurnedBids'
FROM (Users INNER JOIN Bids ON Users.ID=Bids.BidderID)
INNER JOIN Auctions
ON Bids.AuctionID=Auctions.ID
WHERE Users.ID=#UserID
GROUP BY Users.Balance, Users.FreeBids, Auctions.ID
My problam is that it returns no rows if the UserID cant be found on the Bids table.
I know it's something that has to do with my
(Users INNER JOIN Bids ON Users.ID=Bids.BidderID)
But i dont know how to make it return even if the user is no on the Bids table.
You're doing an INNER JOIN, which only returns rows if there are results on both sides of the join. To get what you want, change your WHERE clause like this:
Users LEFT JOIN Bids ON Users.ID=Bids.BidderID
You may also have to change your SELECT statement to handle Bids.Burned being NULL.
If you want to return rows even if there's no matching Auction, then you'll have to make some deeper changes to your query.
My problam is that it returns no rows if the UserID cant be found on the Bids table.
Then INNER JOIN Bids/Auctions should probably be left outer joins. The way you've written it, you're filtering users so that only those in bids and auctions appear.
Left join is the simple answer, but if you're worried about performance I'd consider re-writing it a little bit. For one thing, the order of the columns in the group by matters to performance (although it often doesn't change the results). Generally, you want to group by a column that's indexed first.
Also, it's possible to re-write this query to only have one group by, which will probably speed things up.
Try this out:
with UserBids as (
select
a.ID
, b.BidderID
, ActiveBids = count(case when b.Burned = 0 then 1 end)
, BurnedBids = count(case when b.Burned = 0 then 1 end)
from Bids b
join Auctions a
on a.ID = b.AuctionID
where a.Closed = 0
group by b.BidderID, a.AuctionID
)
select
b.ID
, u.Balance
, u.FreeBids
, b.ActiveBids
, b.BurnedBids
from Users u
left join UserBids b
on b.BidderID = u.ID
where u.ID = #UserID;
If you're not familiar with the with UserBids as..., it's called a CTE (common table expression), and is basically a way to make a one-time use view, and a nice way to structure your queries.