I'm creating an e-commerce benchmark website. I have some problems in my GETSITES stored procedure:
Code:
CREATE PROC USER_S_GETSITES
#CategoryID int,
#List int,
#IsPopular bit,
#MaxID int,
#Status bit,
#Word text
AS
SELECT s.*, c.*, AVG(r.Rate) AS RateAVG, Count(r.ID) AS RateCount
FROM Sites AS s
INNER JOIN Rates AS r ON r.SiteID = s.ID
INNER JOIN Categories AS c ON c.ID = s.CategoryID
WHERE (#CategoryID is NULL) OR s.CategoryID = #CategoryID
AND (#MaxID is NULL) OR s.ID < #MaxID
AND (#Status is NULL) OR s.Status = #Status
AND r.Status=1
AND (#Word is NULL) OR s.Name LIKE #Word OR s.Description LIKE #Word
AND (#IsPopular is NULL) OR s.IsPopular=#IsPopular
ORDER BY
CASE #List WHEN 1 THEN s.ID END ASC,
CASE #List WHEN 2 THEN s.ID END DESC,
CASE #List WHEN 3 THEN RateAVG END DESC,
CASE #List WHEN 4 THEN RateCount END DESC
And my problems :
Msg 8120, Level 16, State 1, Procedure USER_S_GETSITES, Line 9
Column 'Sites.ID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Msg 207, Level 16, State 1, Procedure USER_S_GETSITES, Line 23
Invalid column name 'RateAVG'.
Msg 207, Level 16, State 1, Procedure USER_S_GETSITES, Line 24
Invalid column name 'RateCount'.
I can't solve these problems. What should I do?
Error 1: include column Sites.ID in GROUP BY clause like group by Sites.ID, ....
Error 2: CASE #List WHEN 3 THEN AVG(r.Rate) AS RateAVG END DESC
Error 3: CASE #List WHEN 4 THEN Count(r.ID) AS RateCount END DESC
Related
I have some data that looks like this:
UserID Category
------ --------
1 a
1 b
2 c
3 b
3 a
3 c
A I'd like to binary-encode this grouped by UserID: three different values exist in Category, so a binary encoding would be something like:
UserID encoding
------ --------
1 "1, 1, 0"
2 "0, 0, 1"
3 "1, 1, 1"
i.e., all three values are present for UserID = 3, so the corresponding vector is "1, 1, 1".
Is there a way to do this without doing a bunch of CASE WHEN statements? There may be dozens of possible values in Category
Cross join the distinct users to distinct categories and left join to the table.
Then use GROUP_CONCAT() window function which supports an ORDER BY clause, to collect the 0s and 1s:
WITH
users AS (SELECT DISTINCT UserID FROM tablename),
categories AS (
SELECT DISTINCT Category, DENSE_RANK() OVER (ORDER BY Category) rn
FROM tablename
),
cte AS (
SELECT u.UserID, c.rn,
'"' || GROUP_CONCAT(t.UserID IS NOT NULL)
OVER (PARTITION BY u.UserID ORDER BY c.rn) || '"' encoding
FROM users u CROSS JOIN categories c
LEFT JOIN tablename t
ON t.UserID = u.UserID AND t.Category = c.Category
)
SELECT DISTINCT userID,
FIRST_VALUE(encoding) OVER (PARTITION BY UserID ORDER BY rn DESC) encoding
FROM cte
ORDER BY userID
This will work for any number of categories.
See the demo.
Results:
UserID
encoding
1
"1,1,0"
2
"0,0,1"
3
"1,1,1"
First create an encoding table to explicit establish order of categories in the bitmap:
create table e (Category int, Encoding int);
insert into e values ('a', 1), ('b', 2), ('c', 4);
First generate a list of users u (cross) joined with the encoding table e to get a fully populated (UserId, Category, Encoding) table. Then left join the fully populated table with the user supplied data t. The right hand side t can now be used to drive if we need to set a bit or not:
select
u.UserId,
'"' ||
group_concat(case when t.UserId is null then 0 else 1 end, ', ')
|| '"' 'encoding'
from
(select distinct UserID from t) u
join e
left natural join t
group by 1
order by e.Encoding
and it gives the expected result:
1|"1, 1, 0"
2|"0, 0, 1"
3|"1, 1, 1"
Previously i used a temp table which created outside the procedure and inserted data in SP and used,,,but if i want to create a temp table inside procedure it throwing some errors..to avoid that trying to using cursor..
CREATE OR REPLACE PROCEDURE P_EMAIL_update
AS
I NUMBER := 1;
J NUMBER := 0;
ID NUMBER;
BEGIN
INSERT INTO ENTITY_TEMP --(RN,ENTITY_ID)
SELECT ROWNUM, e.id
FROM entity e, company c
WHERE e.companyid = c.id AND e.status = 3;
SELECT MAX (rn) INTO j FROM ENTITY_TEMP;
WHILE i <= j
LOOP
SELECT entity_id
INTO id
FROM ENTITY_TEMP
WHERE rn = i;
INSERT INTO ACTIONS_EMAIL_MAPPING (ID,
ISACTIVE,
ACTIONNAME,
EMAILFROM,
EMAILSUBJECT,
EMAILBODY
)
(SELECT SEQ_ENT.NEXTVAL,
'N',
ACTIONNAME,
EMAILFROM,
EMAILSUBJECT,
EMAILBODY || var_id
FROM ACTIONS
WHERE ISACTIVE = 'T' AND ACTIONNAME NOT IN ('sample'));
I := I + 1;
END LOOP;
END;
The procedure in your question could be rewritten more simply as:
CREATE OR REPLACE PROCEDURE p_email_update AS
BEGIN
INSERT INTO actions_email_mapping (id, isactive, actionname, emailfrom, emailsubject, emailbody)
SELECT seq_ent.nextval,
'N',
a.actionname,
a.emailfrom,
a.emailsubject,
a.emailbody || ent.id -- assuming var_id is a column of actions
FROM actions a
CROSS JOIN (SELECT e.id
FROM entity e
inner join company c on e.companyid = c.id
WHERE e.status = 3) ent -- maybe this should be an inner join with some join conditions?
WHERE a.isactive = 'T'
AND a.actionname NOT IN ('sample');
END p_email_update;
/
There is no need to reinvent a cross join (which is what your code was doing, in a very roundabout way). There is also no need to store the data in a temp table either, based on what you've told us in your question, since you can just reference the subquery directly in the insert-as-select.
I would question why there aren't any join conditions between your actions table, and the subquery on the entity and company tables, though.
DECLARE
TYPE two_cols IS RECORD
(
family_id family_members.family_id %TYPE,
city family_members.city%TYPE
);
TYPE family_members_t IS TABLE OF two_cols;
l_family_members family_members_t;
BEGIN
SELECT family_id,city
BULK COLLECT INTO l_family_members
FROM (SELECT x.family_id, x.City, x.Member_count,row_number()
OVER (PARTITION BY x.family_id ORDER BY x.Member_count DESC) rn
FROM (SELECT family_id, City, COUNT(*) Member_count
FROM FAMILY_MEMBERS
GROUP BY family_id, City) x) y
WHERE y.rn = 1;
for rec in 1..l_family_members.count
loop
dbms_output.put_line('majority mem of family id'
|| l_family_members.family_id(rec)
|| 'stay in '||l_family_members.city(rec));
end loop;
END;
Error:
ORA-06550: line 23, column 69: PLS-00302: component 'FAMILY_ID' must
be declared ORA-06550: line 23, column 1: PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I am confused at the output line.. I am not getting how to retrieve data from bulk collect as there are two columns in it..how to distinguish them and retrieve them?
you are trying to select 2 columns into 1 record which doesn't work.
depending on your database version, you may be able to select records which then get bulk collected into a table as follows
DECLARE
TYPE two_cols IS RECORD
(
family_id family_members.family_id %TYPE,
city family_members.city%TYPE
);
TYPE family_members_t IS TABLE OF two_cols;
l_family_members family_members_t;
BEGIN
SELECT two_cols(family_id,city )
BULK COLLECT INTO l_family_members
FROM (SELECT x.family_id, x.City, x.Member_count,row_number()
OVER (PARTITION BY x.family_id ORDER BY x.Member_count DESC) rn
FROM (SELECT family_id, City, COUNT(*) Member_count
FROM FAMILY_MEMBERS
GROUP BY family_id, City) x) y
WHERE y.rn = 1;
for rec in 1..l_family_members.count
loop
dbms_output.put_line('majority mem of family id'
|| l_family_members(rec).family_id
|| 'stay in '||l_family_members(rec).city);
end loop;
END;
NB: I also fixed the reference in the output loop to put the (rec) after the table and before column
SELECT B.Value
FROM table1 A WITH (NOLOCK)
INNER JOIN table2 B WITH (NOLOCK) ON A.id = B.id
WHERE
A.Name = 'COMPLETED_AT'
AND CONVERT(smalldatetime, A.Value) < GETDATE() - 30
AND B.Name = 'RESULT'
Getting error message
Conversion failed when converting character string to smalldatetime data type
when executing the above query
Example table structure
ID Name Value
1 Result R12344
1 Completed_At 2015-03-20T06:06:46
2 Result R23445
2 Completed_At 2014-03-20T06:06:46
Column value is of nvarchar(400) datatype
The query result should display the values of result name type which have been made an entry of more than 30 days.
Looking forward in hearing from you.
Avoid Null In Datetime Coloumn field & fix this error (Conversion failed when converting character string to smalldatetime data type.):
First, I give this type get this error:
select * from Tabel1 where ISNULL(Datecoloumn1,**'0'**)!='1900-01-01 00:00:00'
The single quotation Zero is the Problem
I fix like this method :
select * from Tabel1 where ISNULL(Datecoloumn1,**0**) != '1900-01-01 00:00:00'
It works just fine, just changed Table2 to Table1, because you said is a self join.
CREATE TABLE Table1
([ID] int, [Name] varchar(12), [Value] varchar(19))
;
INSERT INTO Table1
([ID], [Name], [Value])
VALUES
(1, 'Result', 'R12344'),
(1, 'Completed_At', '2015-03-20T06:06:46'),
(2, 'Result', 'R23445'),
(2, 'Completed_At', '2014-03-20T06:06:46')
;
SELECT B.Value
FROM table1 A WITH (NOLOCK)
INNER JOIN table1 B WITH (NOLOCK) ON A.id = B.id
WHERE
A.Name = 'COMPLETED_AT'
AND CONVERT(smalldatetime, A.Value) < GETDATE() - 30
AND B.Name = 'RESULT'
Result
Value
R23445
I have created one stored procedure which runs on 5000 users in tbluser table with some filter condition in database.There are 4 filtering condition(FC1,FC2,FC3,FC4).Filtering condition has some ListBox and dropdown list of department and countries.I want output as given below:
ID Name StaffNo department Points
1 KK 111 dep1 2
2 NN 222 dep2 1
3 DD 333 dep3 4
I got ID,Name,StaffNo,department in resultset but not points.
points calculation would be based on filtering condition like
if FC1 matched user gained point 1,if both FC1 and FC2 matched user gained 2 point,if both FC1 ,FC2 and FC3 matched user gained 3 point etc.
--in stored procedure i m using dynamic query
DECLARE #SQL VARCHAR(2000)
SET #SQL = 'SELECT U.UserID, U.StaffNo,U.FirstName+'' ''+ U.LastName AS EmployeeName,''?'' AS Points FROM tblUser U '
SET #SQL = #SQL+' WHERE U.Department in (' + #SqlDepartment + ') '
---------------------Update---------------------------------------
IF #SqlLanguage <> ''
SET #SQL = #SQL+' OR U.UserID IN (SELECT UserID FROM Country WHERE LCValues IN ('+ #SqlLanguage +') )'
IF #SqlAreas <> ''
SET #SQL = #SQL+' OR U.UserID IN (SELECT UserID FROM tblAreas WHERE '+#SqlAreas+')'
---------------------Update---------------------------------------
...other filtering condition
EXEC (#SQL)
all filtering condition are implemented with OR logic.
Have you tried implementing a CASE statement to calculate the "Points"?
--in stored procedure i m using dynamic query
DECLARE #SQL VARCHAR(2000)
SET #SQL = '
SELECT
U.[UserID],
U.[StaffNo],
U.[FirstName]+'' ''+ U.[LastName] AS EmployeeName,
(
CASE WHEN EXISTS(SELECT 1 FROM [Country] WHERE /*Your filter comes in here*/) THEN 1 ELSE 0 END +
CASE WHEN EXISTS(SELECT 1 FROM [tblAreas] WHERE /*Your filter comes in here*/) THEN 1 ELSE 0 END +
CASE WHEN EXISTS(SELECT 1 FROM [OtherTable1] WHERE /*Your filter comes in here*/) THEN 1 ELSE 0 END +
CASE WHEN EXISTS(SELECT 1 FROM [OtherTable2] WHERE /*Your filter comes in here*/) THEN 1 ELSE 0 END
) AS Points
FROM [tblUser] U'
SET #SQL = #SQL+' WHERE U.Department in (' + #SqlDepartment + ') OR'...
...other filtering condition
EXEC (#SQL)
I think you might be better off working out the points in your code rather than in the SQL
The only way I can think of doing it is with UNIONS which would be horrible
You can use left outer join to your filter condition, group by UserID to not get duplicates, add a value of 1 for hits and use coalesce to set 0 for no hits.
Some sample code that shows what I mean using Area and Country as filter condition.
declare #U table (UserID int, Name varchar(50), StaffNo char(3), department char(4))
declare #C table (CountryID int, UserID int)
declare #A table (AreaID int, UserID int)
insert into #U values (1, 'KK', '111', 'dep1')
insert into #U values (2, 'NN', '222', 'dep2')
insert into #U values (3, 'DD', '333', 'dep3')
insert into #C values(1, 1)
insert into #C values(2, 1)
insert into #C values(3, 2)
insert into #C values(3, 3)
insert into #A values(1, 1)
insert into #A values(2, 1)
insert into #A values(3, 2)
select
U.UserID,
U.Name,
U.StaffNo,
U.department,
coalesce(C.Point, 0)+coalesce(A.Point,0) as Points
from #U as U
left outer join
(select UserID, 1 as Point
from #C
-- where ...?
group by UserID) as C
on U.UserID = C.UserID
left outer join
(select UserID, 1 as Point
from #A
-- where ...?
group by UserID) as A
on U.UserID = A.UserID