How do I consolidate this table? - sql

I have a problem that I will try to describe like this. I have a table in PostgreSQL like below
(here's what I have).
Now I'm wrapping my head around how to "merge" or "consolidate" this table to make it look like this one on -> Here's what I want to have.
Multiple rows are the result of having different ID or different value in any column after in general (but I don't need that information anymore, so I may get rid of it without any consequences).
Is there any function or any trick that might bring me desired result?
What I have tried:
select "name"
, "array_agg" [1][1] as math_grade
, "array_agg" [2][2] as history_grade
, "array_agg" [3][3] as geography_grade
from (select "name"
, array_agg(array[math_grade,history_grade,geography_grade])
from temp1234
group by "name") as abc
Here is a example table:
create table temp1234 (id int
, name varchar(50)
, math_grade int
, history_grade int
, geography_grade int)
And example data:
insert into temp1234 values (1, 'John Smith', 3, null, null)
insert into temp1234 values (2, 'John Smith', null, 4, null)
insert into temp1234 values (3, 'John Smith', null, null, 3)
Best Regards

This will give you what you want but I am sure that with more data you will find this query is not covering all you need ? Please do provide more data for more detailed help.
select min(id), name, max(math_grade), max(history_grade), max(geography_grade)
from temp1234
group by name
Here is a demo

Related

Select from a list in a parameter value

I am trying to have a dropdown with subject areas for a school report. The problem I am running into is that in my database, the subjects are grouped by grade and subject instead of just subject. So when I look at gt.standardid in (#SubjectArea) for "Literacy" the standard ids for literacy are (54,61,68,75,88,235) one for each grade level, but I want to have it show me all of them as Literacy. In my parameter "#subjectArea" I have specific values I want to add for each subject area, so for the Label of "Literacy" I want it to select the StandardIds (54,61,68,75,88,235). I am not sure how to accomplish this.
Select
CS.subjectArea
,CS.Name As Group_Name
,GT.Abbreviation
,GT.Name
,GT.standardID
From GradingTask as GT
inner join CurriculumStandard CS
on GT.Standardid = CS.standardid
where GT.ARCHIVED = 0
and GT.standardid in (#SubjectArea)
ORDER BY GT.seq
I would try a cascading parameter approach.
You can have the first parameter be a pre-defined list:
The specific values are not important, but will be used in the next step.
Ideally your IDs would be in a table already, but if not you can use something like this:
declare #SubjectIDs as table
(
[SubjectName] nvarchar(50),
[SubjectID] int
);
insert into #SubjectIDs
(
[SubjectName],
[SubjectID]
)
values
('Literacy', 54),
('Literacy', 61),
('Literacy', 68),
('Literacy', 75),
('Literacy', 88),
('Literacy', 23);
select
SubjectID
from #SubjectIDs
where SubjectName in (#SubjectList);
Make this into a data set. I'm going to call it DS_SubjectIDs.
Make a new hidden or internal parameter called SubjectIDs:
Set it to get its values from the DS_SubjectIDs query:
You can now use the parameter for SubjectIDs in your final query.

Search a text in a column

I have an ASP.NET page and have a table looks like
Create table Test(id int, Users Varchar(MAX));
Insert into Test select 1, 'admin;operator;user1';
Insert into Test select 2, 'superadmin';
Insert into Test select 3, 'superadmin;admin';
Any of Test row can include more than one user, so I am dividing them by semicolon. I want to return the value when the client search in textbox, they will insert only: admin I want to return only the rows which includes admin.
I can not use
select id,Users where Users like '%admin%'
Because in this case the query will return 2nd and 3rd columns which includes superadmin.
How can I get true result?
You shouldn't be storing delimited data, if possible, normalize your database.
As this isn't always possible, you can get what you want with:
where CONCAT(';', Users, ';') like '%;admin;%'
As I mentioned in the comment, you need to fix your design; what you're doing right now is breaking one of the fundamental Normal Form rules. That means, instead, have one row per user:
CREATE TABLE Test (uid int IDENTITY,
id int,
Username nvarchar(128));
INSERT INTO Test (id,
Username)
VALUES (1, N'admin'),
(1, N'operator'),
(1, N'user1'),
(2, N'supermadmin'),
(3, N'superadmin'),
(3, N'admin');
Then you can simply use a = operator:
SELECT *
FROM Test
WHERE Username = N'admin';
I guess you can try below query
SELECT * FROM #tmpTest where users like '%Admin%' AND Users not like 'superAdmin'

How can I intersect two ActiveRecord::Relations on an arbitrary column?

If I have a people table with the following structure and records:
drop table if exists people;
create table people (id int, name varchar(255));
insert into people values (1, "Amy");
insert into people values (2, "Bob");
insert into people values (3, "Chris");
insert into people values (4, "Amy");
insert into people values (5, "Bob");
insert into people values (6, "Chris");
I'd like to find the intersection of people with ids (1, 2, 3) and (4, 5, 6) based on the name column.
In SQL, I'd do something like this:
select
group_concat(id),
group_concat(name)
from people
group by name;
Which returns this result set:
id | name
----|----------
1,4 | Amy,Amy
2,5 | Bob,Bob
3,6 | Chris,Chris
In Rails, I'm not sure how to solve this.
My closest so far is:
a = Model.where(id: [1, 2, 3])
b = Model.where(id: [4, 5, 6])
a_results = a.where(name: b.pluck(:name)).order(:name)
b_results = b.where(name: a.pluck(:name)).order(:name)
a_results.zip(b_results)
This seems to work, but I have the following reservations:
Performance - is this going to perform well in the database?
Lazy enumeration - does calling #zip break lazy enumeration of records?
Duplicates - what will happen if either set contains more than one record for a given name? What will happen if a set contains more than one of the same id?
Any thoughts or suggestions?
Thanks
You can use your normal sql method to get this arbitrary column in ruby like so:
#people = People.select("group_concat(id) as somecolumn1, group_concat(name) as somecolumn2").group("group_concat(id), group_concat(name)")
For each record in #people you will now have somecolumn1/2 attributes.

H2 SQL database - INSERT if the record does not exist

I would like initialize a H2 database, but I am not sure if the records exist. If they exist I don't want to do anything, but if they don't exist I would like to write the default values.
Something like this:
IF 'number of rows in ACCESSLEVELS' = 0
INSERT INTO ACCESSLEVELS VALUES
(0, 'admin'),
(1, 'SEO'),
(2, 'sales director'),
(3, 'manager'),
(4, 'REP')
;
MERGE INTO ACCESSLEVELS
KEY(ID)
VALUES (0, 'admin'),
(1, 'SEO'),
(2, 'sales director'),
(3, 'manager'),
(4, 'REP');
Updates existing rows, and insert rows that don't exist. If no key column is specified, the primary key columns are used to find the row.
If you do not name the columns, their values must be provided as defined in the table. If you prefer to name the columns to be more independent from their order in the table definition, or to avoid having to provide values for all columns when that is not necessary or possible:
MERGE INTO ACCESSLEVELS
(ID, LEVELNAME)
KEY(ID)
VALUES (0, 'admin'),
(1, 'SEO'),
(2, 'sales director'),
(3, 'manager'),
(4, 'REP');
Note that you must include the key column ("ID" in this example) in the column list as well as in the KEY clause.
The following works for MySQL, PostgreSQL, and the H2 database:
drop table ACCESSLEVELS;
create table ACCESSLEVELS(id int, name varchar(255));
insert into ACCESSLEVELS select * from (
select 0, 'admin' union
select 1, 'SEO' union
select 2, 'sales director' union
select 3, 'manager' union
select 4, 'REP'
) x where not exists(select * from ACCESSLEVELS);
To do this you can use MySQL Compatibility Mode in H2 database. Starting from 1.4.197 version it supports the following syntax:
INSERT IGNORE INTO table_name VALUES ...
From this pull request:
INSERT IGNORE is not supported in Regular mode, you have to enable MySQL compatibility mode explicitly by appending ;MODE=MySQL to your database URL or by executing SET MODE MySQL statement.
From official site:
INSERT IGNORE is partially supported and may be used to skip rows with duplicate keys if ON DUPLICATE KEY UPDATE is not specified.
Here is another way:
CREATE TABLE target (C1 VARCHAR(255), C2 VARCHAR(255));
MERGE INTO target AS T USING (SELECT 'foo' C1, 'bar') AS S ON T.C1=S.C1
WHEN NOT MATCHED THEN
INSERT VALUES('foo', 'bar')
When a row in S matches one or more rows in T, do nothing. But when a row in S is not matched, insert it. See "MERGE USING" for more details:
https://www.h2database.com/html/commands.html#merge_using

is this proper sql?

I have some coupon software for an e-commerce site. I want to generate a bunch of coupons at once. It consists of 2 tables. 1 for the coupon, and 1 for the coupon description. The primary key being coupon_id.
Is this the proper way to do the sql? Will the coupon_id match up? Since it is auto-incremented and I am not inputting a number I think it should.
edit: I just rechecked and only the coupon_id field in coupons table is auto incremented not the one in coupon_description
But I'm not sure if using 2 inserts is the proper way.
INSERT INTO coupons (
coupon_id,
coupon_type,
coupon_code,
coupon_amount,
coupon_minimum_order,
coupon_start_date,
coupon_expire_date,
uses_per_coupon,
uses_per_user,
restrict_to_products,
restrict_to_categories,
restrict_to_customers,
coupon_active,
date_created,
date_modified
)
VALUES (
'' , '', '" . substr(md5(uniqid(rand(), true)), 0, 8) ."', '100' , '1' , '06/05/2013' , '06/11/2013' , '1' , '1', '', '', '', '', '', '')
INSERT INTO coupons_description (
coupon_id,
language_id,
coupon_name,
coupon_description
)
VALUES (
'', '1', 'test coupon', 'test'
)
Since only one is auto-incremented, I would insert into that table first, then look up the value and hard-code it into the dependant table. That is:
INSERT INTO coupons (
coupon_type,
coupon_code,
...)
VALUES ('', '" . substr(md5(uniqid(rand(), true)), 0, 8) ."', '100' ,...).
Notice that coupon_id is NOT in the insert statement. The database will assign that itself (assuming the auto-increment is doing what it should). Some databases let you assign this, but I consider that bad form. The problem, then, is finding that record again. I'd use:
select * from coupons order by coupon_id desc;
Unless someone else is adding coupons at the same time, you should see your coupon on top. It would help if you used a unique coupon name or description. Anyway, you'll have to take the assigned ID and then update the table WITHOUT the auto-increment:
INSERT INTO coupons_description (
coupon_id,
language_id,
coupon_name,
coupon_description)
VALUES ('123', '1', 'test coupon', 'test')
Where '123' is really whatever coupon_id you found from the select statement.
But your syntax does look correct, otherwise.
I may go 'coupons' table check biggest coupon_id and specify the new id in insert explicitly. I think this is a safer way to prevent any mismatching.
let's say you have 100 coupon in coupons already. So in your case:
INSERT INTO coupons (
coupon_id,
coupon_type,
...
) VALUES (101 , '', ...);
INSERT INTO coupons_description (
coupon_id,
language_id,
coupon_name,
coupon_description
) VALUES (101, '1', 'test coupon', 'test')
I would suggest either merging the two tables (they appear to have a 1:1 relationship) or changing the description table so that coupon_id is a foreign key of the first table, and remove the autoincrement. After that you can insert into the first table, then instead of trying to guess the id to insert into the second table, you just use select ##identity; (or select scope_identity(); if on sql server)